diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 1ce84a67378..24aa0f85355 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -250,6 +250,11 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { LOGGER.fatal(null, ex); } + // other settings + if (ClientCallback.SIMULATE_BAD_CONNECTION) { + LOGGER.info("Network: bad connection mode enabled"); + } + // DATA PREPARE RepositoryUtil.bootstrapLocalDb(); // re-create database on empty (e.g. after new build cleaned db on startup) diff --git a/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java b/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java index cd15a455d06..9ebe13dc364 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DraftGrid.java @@ -1,15 +1,16 @@ package mage.client.cards; -import mage.cards.CardDimensions; import mage.abilities.icon.CardIconRenderSettings; +import mage.cards.CardDimensions; import mage.cards.MageCard; import mage.client.dialog.PreferencesDialog; +import mage.client.draft.DraftPanel; import mage.client.plugins.impl.Plugins; -import mage.client.util.comparators.CardViewRarityComparator; import mage.client.util.ClientEventType; import mage.client.util.Event; import mage.client.util.Listener; import mage.client.util.audio.AudioManager; +import mage.client.util.comparators.CardViewRarityComparator; import mage.constants.Constants; import mage.view.CardView; import mage.view.CardsView; @@ -28,6 +29,8 @@ public class DraftGrid extends javax.swing.JPanel implements CardEventProducer { private static final Logger logger = Logger.getLogger(DraftGrid.class); + private final DraftPanel parentPanel; + protected final CardEventSource cardEventSource = new CardEventSource(); protected BigCard bigCard; protected MageCard markedCard; @@ -36,22 +39,28 @@ public class DraftGrid extends javax.swing.JPanel implements CardEventProducer { /** * Creates new form DraftGrid */ - public DraftGrid() { + public DraftGrid(DraftPanel panel) { initComponents(); + parentPanel = panel; markedCard = null; emptyGrid = true; // ENABLE picks and other actions - cardEventSource.addListener(new Listener() { - @Override - public void event(Event event) { - if (event.getEventType() == ClientEventType.CARD_DOUBLE_CLICK) { - CardView card = (CardView) event.getSource(); + cardEventSource.addListener(event -> { + if (event.getEventType() == ClientEventType.CARD_DOUBLE_CLICK + || event.getEventType() == ClientEventType.CARD_CLICK) { + // There is a protection against picking too early in DraftPanel logic. + // So, when double clicking early, we do mark the card as selected like + // a single click would. + + CardView card = (CardView) event.getSource(); + if(event.getEventType() == ClientEventType.CARD_DOUBLE_CLICK + && parentPanel.isAllowedToPick() + ) { cardEventSource.fireEvent(card, ClientEventType.DRAFT_PICK_CARD); hidePopup(); AudioManager.playOnDraftSelect(); - } else if (event.getEventType() == ClientEventType.CARD_CLICK) { - CardView card = (CardView) event.getSource(); + } else { MageCard cardPanel = (MageCard) event.getComponent(); if (markedCard != null) { markedCard.setSelected(false); diff --git a/Mage.Client/src/main/java/mage/client/dialog/CustomOptionsDialog.form b/Mage.Client/src/main/java/mage/client/dialog/CustomOptionsDialog.form index a19c7607bde..ee584f9b325 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/CustomOptionsDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/CustomOptionsDialog.form @@ -22,11 +22,26 @@ - + - - + + + + + + + + + + + + + + + + + @@ -35,20 +50,21 @@ - + - + - - + + + + + - - - - + + @@ -76,6 +92,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -173,7 +211,7 @@ - + @@ -185,6 +223,19 @@ + + + + + + + + + + + + + @@ -195,5 +246,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/CustomOptionsDialog.java b/Mage.Client/src/main/java/mage/client/dialog/CustomOptionsDialog.java index f7c9495a3c5..29d4759abfb 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/CustomOptionsDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/CustomOptionsDialog.java @@ -1,12 +1,21 @@ package mage.client.dialog; +import mage.cards.decks.Deck; +import mage.cards.decks.DeckFileFilter; +import mage.cards.decks.importer.DeckImporter; +import mage.client.MageFrame; import mage.constants.MultiplayerAttackOption; import mage.constants.RangeOfInfluence; +import mage.game.GameException; import mage.game.match.MatchOptions; import mage.game.mulligan.MulliganType; import org.apache.log4j.Logger; import javax.swing.*; +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; /** * App GUI: custom options for match/tournament @@ -19,28 +28,49 @@ public class CustomOptionsDialog extends MageDialog { TABLE( PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS, PreferencesDialog.KEY_NEW_TABLE_MULLIGAN_TYPE, - PreferencesDialog.KEY_NEW_TABLE_PLANECHASE + PreferencesDialog.KEY_NEW_TABLE_PLANECHASE, + PreferencesDialog.KEY_NEW_TABLE_EMBLEM_CARDS_ENABLED, + PreferencesDialog.KEY_NEW_TABLE_EMBLEM_CARDS_PER_PLAYER_FILE, + PreferencesDialog.KEY_NEW_TABLE_EMBLEM_CARDS_STARTING_PLAYER_FILE ), TOURNEY( PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_OF_FREE_MULLIGANS, PreferencesDialog.KEY_NEW_TOURNAMENT_MULLIGUN_TYPE, - PreferencesDialog.KEY_NEW_TOURNAMENT_PLANE_CHASE + PreferencesDialog.KEY_NEW_TOURNAMENT_PLANE_CHASE, + PreferencesDialog.KEY_NEW_TOURNAMENT_EMBLEM_CARDS_ENABLED, + PreferencesDialog.KEY_NEW_TOURNAMENT_EMBLEM_CARDS_PER_PLAYER_FILE, + PreferencesDialog.KEY_NEW_TOURNAMENT_EMBLEM_CARDS_STARTING_PLAYER_FILE ); public final String NUMBER_OF_FREE_MULLIGANS; public final String MULLIGAN_TYPE; public final String PLANECHASE; + public final String EMBLEM_CARDS_ENABLED; + public final String EMBLEM_CARDS_PER_PLAYER_FILE; + public final String EMBLEM_CARDS_STARTING_PLAYER_FILE; - SaveLoadKeys(String numberOfFreeMulligans, String mulliganType, String planechase) { + SaveLoadKeys( + String numberOfFreeMulligans, + String mulliganType, + String planechase, + String emblemCardsEnabled, + String emblemCardsPerPlayerFile, + String emblemCardsStartingPlayerFile + ) { NUMBER_OF_FREE_MULLIGANS = numberOfFreeMulligans; MULLIGAN_TYPE = mulliganType; PLANECHASE = planechase; + EMBLEM_CARDS_ENABLED = emblemCardsEnabled; + EMBLEM_CARDS_PER_PLAYER_FILE = emblemCardsPerPlayerFile; + EMBLEM_CARDS_STARTING_PLAYER_FILE = emblemCardsStartingPlayerFile; } } private static final Logger logger = Logger.getLogger(CustomOptionsDialog.class); private final SaveLoadKeys saveLoadKeys; private final JButton openButton; + private final JFileChooser fcSelectEmblemCardsPerPlayer; + private final JFileChooser fcSelectEmblemCardsStartingPlayer; /** * Creates new form NewTableDialog @@ -52,6 +82,12 @@ public class CustomOptionsDialog extends MageDialog { this.spnFreeMulligans.setModel(new SpinnerNumberModel(0, 0, 5, 1)); cbMulliganType.setModel(new DefaultComboBoxModel(MulliganType.values())); this.setModal(true); + fcSelectEmblemCardsPerPlayer = new JFileChooser(); + fcSelectEmblemCardsPerPlayer.setAcceptAllFileFilterUsed(false); + fcSelectEmblemCardsPerPlayer.addChoosableFileFilter(new DeckFileFilter("dck", "XMage's deck files (*.dck)")); + fcSelectEmblemCardsStartingPlayer = new JFileChooser(); + fcSelectEmblemCardsStartingPlayer.setAcceptAllFileFilterUsed(false); + fcSelectEmblemCardsStartingPlayer.addChoosableFileFilter(new DeckFileFilter("dck", "XMage's deck files (*.dck)")); } /** @@ -71,8 +107,18 @@ public class CustomOptionsDialog extends MageDialog { jSeparator2 = new javax.swing.JSeparator(); lblVariantOptions = new javax.swing.JLabel(); chkPlaneChase = new javax.swing.JCheckBox(); + planechaseDescriptionLabel = new javax.swing.JLabel(); jSeparator4 = new javax.swing.JSeparator(); btnOK = new javax.swing.JButton(); + jSeparator3 = new javax.swing.JSeparator(); + chkEmblemCards = new javax.swing.JCheckBox(); + btnEmblemCardsPerPlayer = new javax.swing.JButton(); + txtEmblemCardsPerPlayer = new javax.swing.JTextField(); + lblEmblemCardsPerPlayer = new javax.swing.JLabel(); + btnEmblemCardsStartingPlayer = new javax.swing.JButton(); + txtEmblemCardsStartingPlayer = new javax.swing.JTextField(); + lblEmblemCardsStartingPlayer = new javax.swing.JLabel(); + emblemCardsDescriptionLabel = new javax.swing.JLabel(); setTitle("Custom Options"); @@ -103,7 +149,7 @@ public class CustomOptionsDialog extends MageDialog { lblVariantOptions.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N lblVariantOptions.setText("Variant Options"); - chkPlaneChase.setText("PlaneChase"); + chkPlaneChase.setText("Planechase"); chkPlaneChase.setToolTipText("Use the PlaneChase variant for your game."); chkPlaneChase.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -111,6 +157,9 @@ public class CustomOptionsDialog extends MageDialog { } }); + planechaseDescriptionLabel.setText("Shared planar deck of all implemented planes.
Uses a 9-sided planar die with 2 planeswalk sides and 2 chaos sides.
Some ability text may be incorrect.
Some rules details (such as who controls plane abilities) may be incorrect."); + planechaseDescriptionLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP); + btnOK.setText("OK"); btnOK.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -118,32 +167,90 @@ public class CustomOptionsDialog extends MageDialog { } }); + chkEmblemCards.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N + chkEmblemCards.setText("Emblem Cards (Experimental)"); + chkEmblemCards.setToolTipText("If enabled, select cards to give players emblem copies of"); + chkEmblemCards.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + chkEmblemCardsActionPerformed(evt); + } + }); + + btnEmblemCardsPerPlayer.setText("..."); + btnEmblemCardsPerPlayer.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnEmblemCardsPerPlayerActionPerformed(evt); + } + }); + + txtEmblemCardsPerPlayer.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + txtEmblemCardsPerPlayerActionPerformed(evt); + } + }); + + lblEmblemCardsPerPlayer.setText("Per-Player File"); + lblEmblemCardsPerPlayer.setToolTipText("An emblem of each card in this file is given to each player"); + + btnEmblemCardsStartingPlayer.setText("..."); + btnEmblemCardsStartingPlayer.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnEmblemCardsStartingPlayerActionPerformed(evt); + } + }); + + txtEmblemCardsStartingPlayer.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + txtEmblemCardsStartingPlayerActionPerformed(evt); + } + }); + + lblEmblemCardsStartingPlayer.setText("Starting Player File"); + lblEmblemCardsStartingPlayer.setToolTipText("An emblem of every card in this file is given to the starting player (useful for symmetric effects)"); + + emblemCardsDescriptionLabel.setText("Give players emblems with the abilities of cards.
Note that some abilities may not function correctly from the command zone.
If anything breaks, please report it on GitHub."); + emblemCardsDescriptionLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGroup(layout.createSequentialGroup() .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(jSeparator2) - .addComponent(jSeparator4) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jSeparator4, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(jSeparator2, javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(btnOK)) + .addComponent(jSeparator3) + .addGroup(layout.createSequentialGroup() + .addComponent(txtEmblemCardsPerPlayer) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnEmblemCardsPerPlayer, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(txtEmblemCardsStartingPlayer) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnEmblemCardsStartingPlayer, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() .addComponent(lblMulliganType) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cbMulliganType, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addGroup(layout.createSequentialGroup() .addComponent(lblFreeMulligans) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(spnFreeMulligans, javax.swing.GroupLayout.DEFAULT_SIZE, 126, Short.MAX_VALUE)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(chkPlaneChase) - .addComponent(lblGeneralOptions) - .addComponent(lblVariantOptions)) - .addGap(0, 125, Short.MAX_VALUE)) + .addComponent(spnFreeMulligans)) .addGroup(layout.createSequentialGroup() - .addGap(0, 0, Short.MAX_VALUE) - .addComponent(btnOK))) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lblVariantOptions) + .addComponent(chkPlaneChase) + .addComponent(chkEmblemCards) + .addComponent(lblEmblemCardsPerPlayer) + .addComponent(lblEmblemCardsStartingPlayer) + .addComponent(lblGeneralOptions)) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(planechaseDescriptionLabel) + .addComponent(emblemCardsDescriptionLabel)) .addContainerGap()) ); layout.setVerticalGroup( @@ -166,6 +273,26 @@ public class CustomOptionsDialog extends MageDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(chkPlaneChase) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(planechaseDescriptionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jSeparator3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(chkEmblemCards) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(emblemCardsDescriptionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblEmblemCardsPerPlayer) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(txtEmblemCardsPerPlayer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(btnEmblemCardsPerPlayer, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblEmblemCardsStartingPlayer) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(txtEmblemCardsStartingPlayer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(btnEmblemCardsStartingPlayer, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jSeparator4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) .addComponent(btnOK, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -182,6 +309,14 @@ public class CustomOptionsDialog extends MageDialog { spnFreeMulligans.getAccessibleContext().setAccessibleDescription("Select the number of free mulligans"); spnFreeMulligans.getAccessibleContext().setAccessibleParent(lblFreeMulligans); chkPlaneChase.getAccessibleContext().setAccessibleParent(lblVariantOptions); + planechaseDescriptionLabel.getAccessibleContext().setAccessibleName("Planechase Description"); + planechaseDescriptionLabel.getAccessibleContext().setAccessibleDescription("Shared planar deck of all implemented planes.\nUses a 9-sided planar die with 2 planeswalk sides and 2 chaos sides.\nSome ability text may be incorrect.\nSome rules details (such as who controls plane abilities) may be incorrect."); + planechaseDescriptionLabel.getAccessibleContext().setAccessibleParent(chkPlaneChase); + lblEmblemCardsPerPlayer.getAccessibleContext().setAccessibleParent(chkEmblemCards); + txtEmblemCardsStartingPlayer.getAccessibleContext().setAccessibleDescription(""); + lblEmblemCardsStartingPlayer.getAccessibleContext().setAccessibleParent(chkEmblemCards); + emblemCardsDescriptionLabel.getAccessibleContext().setAccessibleName("Emblem Cards description"); + emblemCardsDescriptionLabel.getAccessibleContext().setAccessibleDescription("Give players emblems with the abilities of cards.\nNote that some abilities may not function correctly from the command zone.\nIf anything breaks, please report it on GitHub."); pack(); }// //GEN-END:initComponents @@ -205,10 +340,50 @@ public class CustomOptionsDialog extends MageDialog { updateActiveCount(); }//GEN-LAST:event_chkPlaneChaseActionPerformed + private void chkEmblemCardsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkEmblemCardsActionPerformed + updateActiveCount(); + }//GEN-LAST:event_chkEmblemCardsActionPerformed + + private void btnEmblemCardsPerPlayerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnEmblemCardsPerPlayerActionPerformed + loadEmblemCardFile(false); + }//GEN-LAST:event_btnEmblemCardsPerPlayerActionPerformed + + private void txtEmblemCardsPerPlayerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtEmblemCardsPerPlayerActionPerformed + + }//GEN-LAST:event_txtEmblemCardsPerPlayerActionPerformed + + private void btnEmblemCardsStartingPlayerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnEmblemCardsStartingPlayerActionPerformed + loadEmblemCardFile(true); + }//GEN-LAST:event_btnEmblemCardsStartingPlayerActionPerformed + + private void txtEmblemCardsStartingPlayerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtEmblemCardsStartingPlayerActionPerformed + + }//GEN-LAST:event_txtEmblemCardsStartingPlayerActionPerformed + public void showDialog() { this.setLocation(150, 100); this.setVisible(true); } + private void loadEmblemCardFile(boolean isStartingPlayer) { + JFileChooser fileChooser = isStartingPlayer ? fcSelectEmblemCardsStartingPlayer : fcSelectEmblemCardsPerPlayer; + JTextField textField = isStartingPlayer ? txtEmblemCardsStartingPlayer : txtEmblemCardsPerPlayer; + String prefKey = isStartingPlayer ? "lastStartingPlayerEmblemCardsFolder" : "lastPerPlayerEmblemCardsFolder"; + + String lastFolder = MageFrame.getPreferences().get(prefKey, ""); + if (!lastFolder.isEmpty()) { + fileChooser.setCurrentDirectory(new File(lastFolder)); + } + int ret = fileChooser.showDialog(this, "Select Emblem Cards"); + if (ret == JFileChooser.APPROVE_OPTION) { + File file = fileChooser.getSelectedFile(); + textField.setText(file.getPath()); + try { + MageFrame.getPreferences().put(prefKey, file.getCanonicalPath()); + } catch (IOException ex) { + } + } + fileChooser.setSelectedFile(null); + } public void onLoadSettings(int version) { @@ -231,6 +406,9 @@ public class CustomOptionsDialog extends MageDialog { this.chkPlaneChase.setSelected(PreferencesDialog.getCachedValue(saveLoadKeys.PLANECHASE + versionStr, "No").equals("Yes")); this.spnFreeMulligans.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(saveLoadKeys.NUMBER_OF_FREE_MULLIGANS + versionStr, "0"))); this.cbMulliganType.setSelectedItem(MulliganType.valueByName(PreferencesDialog.getCachedValue(saveLoadKeys.MULLIGAN_TYPE + versionStr, MulliganType.GAME_DEFAULT.toString()))); + this.chkEmblemCards.setSelected(PreferencesDialog.getCachedValue(saveLoadKeys.EMBLEM_CARDS_ENABLED + versionStr, "No").equals("Yes")); + this.txtEmblemCardsPerPlayer.setText(PreferencesDialog.getCachedValue(saveLoadKeys.EMBLEM_CARDS_PER_PLAYER_FILE, "")); + this.txtEmblemCardsStartingPlayer.setText(PreferencesDialog.getCachedValue(saveLoadKeys.EMBLEM_CARDS_STARTING_PLAYER_FILE, "")); updateActiveCount(); } @@ -250,6 +428,10 @@ public class CustomOptionsDialog extends MageDialog { PreferencesDialog.saveValue(saveLoadKeys.NUMBER_OF_FREE_MULLIGANS + versionStr, Integer.toString(options.getFreeMulligans())); PreferencesDialog.saveValue(saveLoadKeys.MULLIGAN_TYPE + versionStr, options.getMulliganType().toString()); PreferencesDialog.saveValue(saveLoadKeys.PLANECHASE + versionStr, options.isPlaneChase() ? "Yes" : "No"); + PreferencesDialog.saveValue(saveLoadKeys.EMBLEM_CARDS_ENABLED + versionStr, + !(options.getGlobalEmblemCards().isEmpty() && options.getPerPlayerEmblemCards().isEmpty()) ? "Yes" : "No"); + PreferencesDialog.saveValue(saveLoadKeys.EMBLEM_CARDS_PER_PLAYER_FILE + versionStr, txtEmblemCardsPerPlayer.getText()); + PreferencesDialog.saveValue(saveLoadKeys.EMBLEM_CARDS_STARTING_PLAYER_FILE + versionStr, txtEmblemCardsStartingPlayer.getText()); } /** @@ -259,6 +441,42 @@ public class CustomOptionsDialog extends MageDialog { options.setFreeMulligans((Integer) spnFreeMulligans.getValue()); options.setMullgianType((MulliganType) cbMulliganType.getSelectedItem()); options.setPlaneChase(chkPlaneChase.isSelected()); + if (chkEmblemCards.isSelected()) { + if (!txtEmblemCardsPerPlayer.getText().isEmpty()) { + Deck perPlayerEmblemDeck = null; + try { + perPlayerEmblemDeck = Deck.load(DeckImporter.importDeckFromFile(txtEmblemCardsPerPlayer.getText(), true), true, true); + } catch (GameException e1) { + JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE); + } + if (perPlayerEmblemDeck != null) { + perPlayerEmblemDeck.clearLayouts(); + options.setPerPlayerEmblemCards(perPlayerEmblemDeck.getDeckCardLists().getCards()); + } + else { + options.setPerPlayerEmblemCards(Collections.emptySet()); + } + } + if (!txtEmblemCardsStartingPlayer.getText().isEmpty()) { + Deck startingPlayerEmblemDeck = null; + try { + startingPlayerEmblemDeck = Deck.load(DeckImporter.importDeckFromFile(txtEmblemCardsStartingPlayer.getText(), true), true, true); + } catch (GameException e1) { + JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE); + } + if (startingPlayerEmblemDeck != null) { + startingPlayerEmblemDeck.clearLayouts(); + options.setGlobalEmblemCards(startingPlayerEmblemDeck.getDeckCardLists().getCards()); + } + else { + options.setGlobalEmblemCards(Collections.emptySet()); + } + } + } + else { + options.setPerPlayerEmblemCards(Collections.emptySet()); + options.setGlobalEmblemCards(Collections.emptySet()); + } } public void updateActiveCount() { @@ -266,6 +484,7 @@ public class CustomOptionsDialog extends MageDialog { if ((Integer)spnFreeMulligans.getValue() > 0) activeCount++; if (!cbMulliganType.getSelectedItem().toString().equals(MulliganType.GAME_DEFAULT.toString())) activeCount++; if (chkPlaneChase.isSelected()) activeCount++; + if (chkEmblemCards.isSelected()) activeCount++; if (activeCount == 0) { openButton.setText("Custom Options..."); } @@ -275,15 +494,25 @@ public class CustomOptionsDialog extends MageDialog { } // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton btnEmblemCardsPerPlayer; + private javax.swing.JButton btnEmblemCardsStartingPlayer; private javax.swing.JButton btnOK; private javax.swing.JComboBox cbMulliganType; + private javax.swing.JCheckBox chkEmblemCards; private javax.swing.JCheckBox chkPlaneChase; + private javax.swing.JLabel emblemCardsDescriptionLabel; private javax.swing.JSeparator jSeparator2; + private javax.swing.JSeparator jSeparator3; private javax.swing.JSeparator jSeparator4; + private javax.swing.JLabel lblEmblemCardsPerPlayer; + private javax.swing.JLabel lblEmblemCardsStartingPlayer; private javax.swing.JLabel lblFreeMulligans; private javax.swing.JLabel lblGeneralOptions; private javax.swing.JLabel lblMulliganType; private javax.swing.JLabel lblVariantOptions; + private javax.swing.JLabel planechaseDescriptionLabel; private javax.swing.JSpinner spnFreeMulligans; + private javax.swing.JTextField txtEmblemCardsPerPlayer; + private javax.swing.JTextField txtEmblemCardsStartingPlayer; // 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 5117a5ea841..3ee7017fdf1 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form @@ -45,7 +45,7 @@ - + @@ -134,14 +134,14 @@ - + - + @@ -151,13 +151,13 @@ - + - + @@ -280,27 +280,25 @@ - - - - - - - + + + + + + - - + @@ -317,8 +315,6 @@ - - @@ -341,9 +337,6 @@ - - - @@ -351,9 +344,6 @@ - - - @@ -361,9 +351,6 @@ - - - @@ -371,9 +358,6 @@ - - - @@ -381,19 +365,6 @@ - - - - - - - - - - - - - @@ -401,9 +372,6 @@ - - - @@ -411,9 +379,6 @@ - - - @@ -438,9 +403,6 @@ - - - @@ -4237,9 +4199,6 @@ - - - @@ -4248,9 +4207,6 @@ - - - @@ -4258,9 +4214,6 @@ - - - @@ -4271,9 +4224,6 @@ - - - @@ -4281,9 +4231,6 @@ - - - @@ -4294,9 +4241,6 @@ - - - @@ -4307,9 +4251,6 @@ - - - @@ -4320,9 +4261,6 @@ - - - @@ -4333,9 +4271,6 @@ - - - @@ -4346,9 +4281,6 @@ - - - @@ -4668,9 +4600,6 @@ - - - @@ -4681,9 +4610,6 @@ - - - @@ -4774,36 +4700,24 @@ - - - - - - - - - - - - @@ -4869,9 +4783,6 @@ - - - @@ -5993,9 +5904,6 @@ - - - @@ -6010,17 +5918,11 @@ - - - - - - @@ -6330,7 +6232,7 @@ - + @@ -6378,7 +6280,7 @@ - + @@ -6410,9 +6312,6 @@ - - - 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 b0f47b6eae7..91ede5a06a4 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -49,7 +49,6 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_DISPLAY_LIVE_ON_AVATAR = "displayLiveOnAvatar"; public static final String KEY_SHOW_ABILITY_PICKER_FORCED = "showAbilityPicker"; public static final String KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS = "gameAllowRequestShowHandCards"; - public static final String KEY_GAME_SHOW_STORM_COUNTER = "gameShowStormCounter"; public static final String KEY_GAME_CONFIRM_EMPTY_MANA_POOL = "gameConfirmEmptyManaPool"; public static final String KEY_GAME_ASK_MOVE_TO_GRAVE_ORDER = "gameAskMoveToGraveORder"; public static final String KEY_GAME_USE_PROFANITY_FILTER = "gameUseProfanityFilter"; @@ -214,6 +213,9 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_NEW_TABLE_MINIMUM_RATING = "newTableMinimumRating"; public static final String KEY_NEW_TABLE_RATED = "newTableRated"; public static final String KEY_NEW_TABLE_EDH_POWER_LEVEL = "newTableEdhPowerLevel"; + public static final String KEY_NEW_TABLE_EMBLEM_CARDS_ENABLED = "newTableEmblemCardsEnabled"; + public static final String KEY_NEW_TABLE_EMBLEM_CARDS_PER_PLAYER_FILE = "newTableEmblemCardsPerPlayerFile"; + public static final String KEY_NEW_TABLE_EMBLEM_CARDS_STARTING_PLAYER_FILE = "newTableEmblemCardsStartingPlayerFile"; // pref setting for new tournament dialog public static final String KEY_NEW_TOURNAMENT_NAME = "newTournamentName"; @@ -238,6 +240,9 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_NEW_TOURNAMENT_QUIT_RATIO = "newTournamentQuitRatio"; public static final String KEY_NEW_TOURNAMENT_MINIMUM_RATING = "newTournamentMinimumRating"; public static final String KEY_NEW_TOURNAMENT_RATED = "newTournamentRated"; + public static final String KEY_NEW_TOURNAMENT_EMBLEM_CARDS_ENABLED = "newTournamentEmblemCardsEnabled"; + public static final String KEY_NEW_TOURNAMENT_EMBLEM_CARDS_PER_PLAYER_FILE = "newTournamentEmblemCardsPerPlayerFile"; + public static final String KEY_NEW_TOURNAMENT_EMBLEM_CARDS_STARTING_PLAYER_FILE = "newTournamentEmblemCardsStartingPlayerFile"; // Settings for auto-choosing targets public static final String KEY_AUTO_TARGET_LEVEL = "autoTargetLevel"; @@ -439,7 +444,6 @@ public class PreferencesDialog extends javax.swing.JDialog { displayLifeOnAvatar = new javax.swing.JCheckBox(); showAbilityPickerForced = new javax.swing.JCheckBox(); cbAllowRequestToShowHandCards = new javax.swing.JCheckBox(); - cbShowStormCounter = new javax.swing.JCheckBox(); cbConfirmEmptyManaPool = new javax.swing.JCheckBox(); cbAskMoveToGraveOrder = new javax.swing.JCheckBox(); lblTargetAutoChoose = new javax.swing.JLabel(); @@ -644,22 +648,22 @@ public class PreferencesDialog extends javax.swing.JDialog { main_gamelog.add(cbGameLogShowTurnInfo); cbGameLogAutoSave.setSelected(true); - cbGameLogAutoSave.setText("Save game logs (to \"../Mage.Client/gamelogs/\" directory)"); + cbGameLogAutoSave.setText("Save game logs (dest folder: \"..\\xmage\\mage-client\\gamelogs\")"); cbGameLogAutoSave.setToolTipText("The logs of all your games will be saved to the mentioned folder if this option is switched on."); main_gamelog.add(cbGameLogAutoSave); cbDraftLogAutoSave.setSelected(true); - cbDraftLogAutoSave.setText("Save draft logs (to \"../Mage.Client/gamelogs/\" directory)"); + cbDraftLogAutoSave.setText("Save draft logs (dest folder: \"..\\xmage\\mage-client\\gamelogs\")"); cbDraftLogAutoSave.setToolTipText("The logs of all your games will be saved to the mentioned folder if this option is switched on."); cbDraftLogAutoSave.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); main_gamelog.add(cbDraftLogAutoSave); cbLimitedDeckAutoSave.setSelected(true); - cbLimitedDeckAutoSave.setText("Save limited decks on submit (to \"../Mage.Client/gamelogs/\" directory)"); + cbLimitedDeckAutoSave.setText("Save limited decks on submit (dest folder: \"..\\xmage\\mage-client\\gamelogs\")"); cbLimitedDeckAutoSave.setToolTipText("A .dck file for each limited tournament will be saved to the mentioned folder if this option is switched on."); main_gamelog.add(cbLimitedDeckAutoSave); - cbGameJsonLogAutoSave.setText("Save JSON game logs (to \"../Mage.Client/gamelogsJson/\" directory)"); + cbGameJsonLogAutoSave.setText("Save JSON game logs (dest folder: \"..\\xmage\\mage-client\\gamelogs\")"); cbGameJsonLogAutoSave.setToolTipText("The JSON logs of all your games will be saved to the mentioned folder if this option is switched on."); main_gamelog.add(cbGameJsonLogAutoSave); @@ -733,74 +737,30 @@ public class PreferencesDialog extends javax.swing.JDialog { nonLandPermanentsInOnePile.setSelected(true); nonLandPermanentsInOnePile.setText("Put non-land permanents in same row as creatures"); nonLandPermanentsInOnePile.setToolTipText("If activated, all non land permanents are shown in one row.
\nFirst creatures than other permanents. If not activated, creatures are
\nshown in a separate row."); - nonLandPermanentsInOnePile.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - nonLandPermanentsInOnePileActionPerformed(evt); - } - }); showPlayerNamesPermanently.setSelected(true); showPlayerNamesPermanently.setText("Show player names on avatar permanently"); showPlayerNamesPermanently.setToolTipText("Instead showing the names only if you hover over the avatar with the mouse, the name is shown all the time."); - showPlayerNamesPermanently.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - showPlayerNamesPermanentlyActionPerformed(evt); - } - }); displayLifeOnAvatar.setSelected(true); displayLifeOnAvatar.setText("Display life on avatar image"); displayLifeOnAvatar.setToolTipText("Display the player's life over its avatar image."); - displayLifeOnAvatar.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - displayLifeOnAvatarActionPerformed(evt); - } - }); showAbilityPickerForced.setSelected(true); 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.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - showAbilityPickerForcedActionPerformed(evt); - } - }); cbAllowRequestToShowHandCards.setSelected(true); cbAllowRequestToShowHandCards.setText("Allow requests from players and spectators to show your hand cards"); cbAllowRequestToShowHandCards.setToolTipText("This is the default setting used for your matches. If activated other players or spectators
\nof your match can send a request so you can allow them to see your hand cards."); - cbAllowRequestToShowHandCards.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbAllowRequestToShowHandCardsActionPerformed(evt); - } - }); - - cbShowStormCounter.setSelected(true); - cbShowStormCounter.setText("Show the number of spell casts during the current turn"); - cbShowStormCounter.setToolTipText("Adds a little box left to the short keys line with the number
\nof spells already cast during the current turn (storm counter)."); - cbShowStormCounter.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbShowStormCounterActionPerformed(evt); - } - }); cbConfirmEmptyManaPool.setSelected(true); cbConfirmEmptyManaPool.setText("Confirm if you want to pass a phase/step but there is still mana in your mana pool"); cbConfirmEmptyManaPool.setToolTipText("If activated you get a confirm message if you pass priority while stack is empty
\n and you still have mana in your mana pool."); - cbConfirmEmptyManaPool.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbConfirmEmptyManaPoolActionPerformed(evt); - } - }); cbAskMoveToGraveOrder.setSelected(true); cbAskMoveToGraveOrder.setText("Ask player for setting order cards go to graveyard"); cbAskMoveToGraveOrder.setToolTipText("If activated and multiple cards go to the graveyard at the same time
\nthe player is asked to set the order of the cards."); - cbAskMoveToGraveOrder.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbAskMoveToGraveOrderActionPerformed(evt); - } - }); lblTargetAutoChoose.setText("Auto-choose targets for player:"); lblTargetAutoChoose.setToolTipText("\nWhen there is only one possible outcome for targeting, the targets can be chosen for you.\n
\nNone: All targeting must be done by the player.\n
\nMost: All targeting other than feel-bad effects (discarding, destroy, sacrifice, exile) that target you, a card you own, or a permanent/spell you control.\n
\nAll: All targeting that can be automated will be."); @@ -808,34 +768,27 @@ public class PreferencesDialog extends javax.swing.JDialog { cbTargetAutoChooseLevel.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Off", "Most", "All" })); cbTargetAutoChooseLevel.setSelectedIndex(1); cbTargetAutoChooseLevel.setToolTipText(lblTargetAutoChoose.getToolTipText()); - cbTargetAutoChooseLevel.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbTargetAutoChooseLevelActionPerformed(evt); - } - }); org.jdesktop.layout.GroupLayout main_gameLayout = new org.jdesktop.layout.GroupLayout(main_game); main_game.setLayout(main_gameLayout); main_gameLayout.setHorizontalGroup( main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) - .add(main_gameLayout.createSequentialGroup() - .addContainerGap() - .add(lblTargetAutoChoose) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(cbTargetAutoChooseLevel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .add(displayLifeOnAvatar, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(main_gameLayout.createSequentialGroup() .add(main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(main_gameLayout.createSequentialGroup() + .addContainerGap() + .add(lblTargetAutoChoose) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(cbTargetAutoChooseLevel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .add(cbAskMoveToGraveOrder, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 596, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING, false) .add(showPlayerNamesPermanently, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(nonLandPermanentsInOnePile, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(cbConfirmEmptyManaPool, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(cbAllowRequestToShowHandCards, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(cbShowStormCounter, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(showAbilityPickerForced))) - .add(0, 315, Short.MAX_VALUE)) + .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); main_gameLayout.setVerticalGroup( main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) @@ -850,8 +803,6 @@ public class PreferencesDialog extends javax.swing.JDialog { .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(cbAllowRequestToShowHandCards) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(cbShowStormCounter) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(cbConfirmEmptyManaPool) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(cbAskMoveToGraveOrder) @@ -1443,108 +1394,58 @@ public class PreferencesDialog extends javax.swing.JDialog { cbStopAttack.setText("STOP skips on declare attackers if attackers are available"); cbStopAttack.setToolTipText(""); cbStopAttack.setActionCommand(""); - cbStopAttack.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbStopAttackActionPerformed(evt); - } - }); phases_stopSettings.add(cbStopAttack); cbStopBlockWithAny.setSelected(true); cbStopBlockWithAny.setText("STOP skips on declare blockers if ANY blockers are available"); cbStopBlockWithAny.setToolTipText(""); cbStopBlockWithAny.setActionCommand(""); - cbStopBlockWithAny.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbStopBlockWithAnyActionPerformed(evt); - } - }); phases_stopSettings.add(cbStopBlockWithAny); cbStopBlockWithZero.setText("STOP skips on declare blockers if ZERO blockers are available"); cbStopBlockWithZero.setToolTipText(""); cbStopBlockWithZero.setActionCommand(""); - cbStopBlockWithZero.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbStopBlockWithZeroActionPerformed(evt); - } - }); phases_stopSettings.add(cbStopBlockWithZero); cbStopOnNewStackObjects.setText("Skip to STACK resolved (F10): stop on new objects added (on) or stop until empty (off)"); cbStopOnNewStackObjects.setToolTipText(""); cbStopOnNewStackObjects.setActionCommand(""); cbStopOnNewStackObjects.setPreferredSize(new java.awt.Dimension(300, 25)); - cbStopOnNewStackObjects.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbStopOnNewStackObjectsActionPerformed(evt); - } - }); phases_stopSettings.add(cbStopOnNewStackObjects); cbStopOnAllMain.setText("Skip to MAIN step (F7): stop on any main steps (on) or stop on your main step (off)"); cbStopOnAllMain.setToolTipText(""); cbStopOnAllMain.setActionCommand(""); - cbStopOnAllMain.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbStopOnAllMainActionPerformed(evt); - } - }); phases_stopSettings.add(cbStopOnAllMain); cbStopOnAllEnd.setText("Skip to END step (F5): stop on any end steps (on) or stop on opponents end step (off)"); cbStopOnAllEnd.setToolTipText(""); cbStopOnAllEnd.setActionCommand(""); cbStopOnAllEnd.setPreferredSize(new java.awt.Dimension(300, 25)); - cbStopOnAllEnd.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbStopOnAllEndActionPerformed(evt); - } - }); phases_stopSettings.add(cbStopOnAllEnd); cbPassPriorityCast.setText("Pass priority automatically after you have put a spell on the stack"); cbPassPriorityCast.setToolTipText("If activated the system passes priority automatically for you if you have put a spell on the stack."); cbPassPriorityCast.setActionCommand(""); cbPassPriorityCast.setPreferredSize(new java.awt.Dimension(300, 25)); - cbPassPriorityCast.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbPassPriorityCastActionPerformed(evt); - } - }); phases_stopSettings.add(cbPassPriorityCast); cbPassPriorityActivation.setText("Pass priority automatically after you have put an activated ability on the stack"); cbPassPriorityActivation.setToolTipText("If activated the system passes priority for you automatically after you have put an activated ability on the stack."); cbPassPriorityActivation.setActionCommand(""); cbPassPriorityActivation.setPreferredSize(new java.awt.Dimension(300, 25)); - cbPassPriorityActivation.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbPassPriorityActivationActionPerformed(evt); - } - }); phases_stopSettings.add(cbPassPriorityActivation); cbAutoOrderTrigger.setText("TRIGGERS: auto-choose triggers order for same rule texts (put same triggers to the stack at default order)"); cbAutoOrderTrigger.setToolTipText("If you put same triggers with same texts on the stack then auto-choose their order.
\nYou can change that settings anytime at the game."); cbAutoOrderTrigger.setActionCommand(""); cbAutoOrderTrigger.setPreferredSize(new java.awt.Dimension(300, 25)); - cbAutoOrderTrigger.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbAutoOrderTriggerActionPerformed(evt); - } - }); phases_stopSettings.add(cbAutoOrderTrigger); cbUseSameSettingsForReplacementEffect.setText("REPLACEMENT EFFECTS: use same auto-choose settings for same cards (choose replacement effects order dialog)"); cbUseSameSettingsForReplacementEffect.setToolTipText("If you setup auto-choose for one object/card then it will be applied for all other objects with same name.
\nYou can change that settings anytime at the game."); cbUseSameSettingsForReplacementEffect.setActionCommand(""); cbUseSameSettingsForReplacementEffect.setPreferredSize(new java.awt.Dimension(300, 25)); - cbUseSameSettingsForReplacementEffect.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbUseSameSettingsForReplacementEffectActionPerformed(evt); - } - }); phases_stopSettings.add(cbUseSameSettingsForReplacementEffect); org.jdesktop.layout.GroupLayout tabPhasesLayout = new org.jdesktop.layout.GroupLayout(tabPhases); @@ -1781,12 +1682,6 @@ public class PreferencesDialog extends javax.swing.JDialog { } }); - txtBackgroundImagePath.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - txtBackgroundImagePathActionPerformed(evt); - } - }); - btnBrowseBackgroundImage.setText("Browse..."); btnBrowseBackgroundImage.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -1794,12 +1689,6 @@ public class PreferencesDialog extends javax.swing.JDialog { } }); - txtBattlefieldImagePath.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - txtBattlefieldImagePathActionPerformed(evt); - } - }); - btnBrowseBattlefieldImage.setText("Browse..."); btnBrowseBattlefieldImage.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -1892,38 +1781,18 @@ public class PreferencesDialog extends javax.swing.JDialog { cbEnableGameSounds.setText("Enable game sounds"); cbEnableGameSounds.setToolTipText("Sounds that will be played for certain actions (e.g. play land, attack, etc.) during the game."); - cbEnableGameSounds.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbEnableGameSoundsActionPerformed(evt); - } - }); sounds_clips.add(cbEnableGameSounds); cbEnableDraftSounds.setText("Enable draft sounds"); cbEnableDraftSounds.setToolTipText("Sounds that will be played during drafting for card picking or warining if time runs out."); - cbEnableDraftSounds.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbEnableDraftSoundsActionPerformed(evt); - } - }); sounds_clips.add(cbEnableDraftSounds); cbEnableSkipButtonsSounds.setText("Enable skip button sounds"); cbEnableSkipButtonsSounds.setToolTipText("Sounds that will be played if a priority skip action (F4/F5/F7/F9) or cancel skip action (F3) is used."); - cbEnableSkipButtonsSounds.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbEnableSkipButtonsSoundsActionPerformed(evt); - } - }); sounds_clips.add(cbEnableSkipButtonsSounds); cbEnableOtherSounds.setText("Enable other sounds"); cbEnableOtherSounds.setToolTipText("Sounds that will be played for actions outside of games (e.g. whisper, player joins your game, player submits a deck ...)."); - cbEnableOtherSounds.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbEnableOtherSoundsActionPerformed(evt); - } - }); sounds_clips.add(cbEnableOtherSounds); sounds_backgroundMusic.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Music")); @@ -1940,12 +1809,6 @@ public class PreferencesDialog extends javax.swing.JDialog { jLabel16.setText("Playing from folder:"); jLabel16.setToolTipText(""); - txtBattlefieldIBGMPath.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - txtBattlefieldIBGMPathActionPerformed(evt); - } - }); - btnBattlefieldBGMBrowse.setText("Browse..."); btnBattlefieldBGMBrowse.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -2518,28 +2381,11 @@ public class PreferencesDialog extends javax.swing.JDialog { lblProxyPort.setText("Port:"); - txtProxyPort.addKeyListener(new java.awt.event.KeyAdapter() { - public void keyTyped(java.awt.event.KeyEvent evt) { - txtProxyPortkeyTyped(evt); - } - }); - lblProxyUserName.setText("User Name:"); lblProxyPassword.setText("Password:"); - txtPasswordField.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - txtPasswordFieldActionPerformed(evt); - } - }); - rememberPswd.setText("Remember Password"); - rememberPswd.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - rememberPswdActionPerformed(evt); - } - }); jLabel11.setFont(new java.awt.Font("Tahoma", 2, 10)); // NOI18N jLabel11.setText("Note: password won't be encrypted!"); @@ -2804,12 +2650,6 @@ public class PreferencesDialog extends javax.swing.JDialog { lbSelectLabel.setPreferredSize(new java.awt.Dimension(110, 16)); lbSelectLabel.setVerticalTextPosition(javax.swing.SwingConstants.TOP); - cbTheme.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbThemeActionPerformed(evt); - } - }); - lbThemeHint.setText("Requires a restart to apply new theme."); org.jdesktop.layout.GroupLayout themesCategoryLayout = new org.jdesktop.layout.GroupLayout(themesCategory); @@ -2823,7 +2663,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .add(themesCategoryLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(lbThemeHint) .add(cbTheme, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 303, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(303, Short.MAX_VALUE)) + .addContainerGap(360, Short.MAX_VALUE)) ); themesCategoryLayout.setVerticalGroup( themesCategoryLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) @@ -2840,7 +2680,7 @@ public class PreferencesDialog extends javax.swing.JDialog { tabThemes.setLayout(tabThemesLayout); tabThemesLayout.setHorizontalGroup( tabThemesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) - .add(0, 750, Short.MAX_VALUE) + .add(0, 807, Short.MAX_VALUE) .add(tabThemesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(tabThemesLayout.createSequentialGroup() .addContainerGap() @@ -2897,7 +2737,7 @@ public class PreferencesDialog extends javax.swing.JDialog { layout.setVerticalGroup( layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(layout.createSequentialGroup() - .add(tabsPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 584, Short.MAX_VALUE) + .add(tabsPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) .add(saveButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 30, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) @@ -2920,7 +2760,6 @@ public class PreferencesDialog extends javax.swing.JDialog { save(prefs, dialog.displayLifeOnAvatar, KEY_DISPLAY_LIVE_ON_AVATAR, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.showAbilityPickerForced, KEY_SHOW_ABILITY_PICKER_FORCED, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbAllowRequestToShowHandCards, KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true", "false", UPDATE_CACHE_POLICY); - save(prefs, dialog.cbShowStormCounter, KEY_GAME_SHOW_STORM_COUNTER, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbConfirmEmptyManaPool, KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbAskMoveToGraveOrder, KEY_GAME_ASK_MOVE_TO_GRAVE_ORDER, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbGameLogShowTurnInfo, KEY_GAME_LOG_SHOW_TURN_INFO, "true", "false", UPDATE_CACHE_POLICY); @@ -3097,19 +2936,6 @@ public class PreferencesDialog extends javax.swing.JDialog { this.showProxySettings(); }//GEN-LAST:event_cbProxyTypeActionPerformed - private void txtPasswordFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtPasswordFieldActionPerformed - }//GEN-LAST:event_txtPasswordFieldActionPerformed - - private void txtProxyPortkeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtProxyPortkeyTyped - }//GEN-LAST:event_txtProxyPortkeyTyped - - private void rememberPswdActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rememberPswdActionPerformed - }//GEN-LAST:event_rememberPswdActionPerformed - - private void cbEnableGameSoundsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbEnableGameSoundsActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbEnableGameSoundsActionPerformed - private void cbEnableBattlefieldBGMActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbEnableBattlefieldBGMActionPerformed if (cbEnableBattlefieldBGM.isSelected()) { txtBattlefieldIBGMPath.setEnabled(true); @@ -3206,20 +3032,7 @@ public class PreferencesDialog extends javax.swing.JDialog { } } - private void txtBackgroundImagePathActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtBackgroundImagePathActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_txtBackgroundImagePathActionPerformed - - private void txtBattlefieldImagePathActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtBattlefieldImagePathActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_txtBattlefieldImagePathActionPerformed - - private void txtBattlefieldIBGMPathActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtBattlefieldIBGMPathActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_txtBattlefieldIBGMPathActionPerformed - private void btnBattlefieldBGMBrowseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnBattlefieldBGMBrowseActionPerformed - // TODO add your handling code here: int returnVal = fc.showOpenDialog(PreferencesDialog.this); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = fc.getSelectedFile(); @@ -3227,78 +3040,10 @@ public class PreferencesDialog extends javax.swing.JDialog { } }//GEN-LAST:event_btnBattlefieldBGMBrowseActionPerformed - private void nonLandPermanentsInOnePileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nonLandPermanentsInOnePileActionPerformed - - }//GEN-LAST:event_nonLandPermanentsInOnePileActionPerformed - - private void showPlayerNamesPermanentlyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showPlayerNamesPermanentlyActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_showPlayerNamesPermanentlyActionPerformed - private void showCardNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showCardNameActionPerformed }//GEN-LAST:event_showCardNameActionPerformed - private void showAbilityPickerForcedActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showAbilityPickerForcedActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_showAbilityPickerForcedActionPerformed - - private void cbEnableOtherSoundsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbEnableOtherSoundsActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbEnableOtherSoundsActionPerformed - - private void cbStopAttackActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbStopAttackActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbStopAttackActionPerformed - - private void cbStopBlockWithAnyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbStopBlockWithAnyActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbStopBlockWithAnyActionPerformed - - private void cbStopOnAllMainActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbStopOnAllMainActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbStopOnAllMainActionPerformed - - private void cbStopOnAllEndActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbStopOnAllEndActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbStopOnAllEndActionPerformed - - private void cbEnableDraftSoundsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbEnableDraftSoundsActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbEnableDraftSoundsActionPerformed - - private void cbEnableSkipButtonsSoundsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbEnableSkipButtonsSoundsActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbEnableSkipButtonsSoundsActionPerformed - - private void cbAllowRequestToShowHandCardsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbAllowRequestToShowHandCardsActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbAllowRequestToShowHandCardsActionPerformed - - private void cbShowStormCounterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbShowStormCounterActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbShowStormCounterActionPerformed - - private void cbConfirmEmptyManaPoolActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbConfirmEmptyManaPoolActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbConfirmEmptyManaPoolActionPerformed - - private void cbAskMoveToGraveOrderActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbAskMoveToGraveOrderActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbAskMoveToGraveOrderActionPerformed - - private void cbPassPriorityCastActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbPassPriorityCastActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbPassPriorityCastActionPerformed - - private void cbPassPriorityActivationActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbPassPriorityActivationActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbPassPriorityActivationActionPerformed - - private void cbAutoOrderTriggerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbAutoOrderTriggerActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbAutoOrderTriggerActionPerformed - private void bttnResetControlsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bttnResetControlsActionPerformed getKeybindButtons().forEach((bttn) -> { String id = bttn.getKey(); @@ -3316,18 +3061,6 @@ public class PreferencesDialog extends javax.swing.JDialog { }//GEN-LAST:event_cbBattlefieldFeedbackColorizingModeActionPerformed - private void displayLifeOnAvatarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_displayLifeOnAvatarActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_displayLifeOnAvatarActionPerformed - - private void cbStopOnNewStackObjectsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbStopOnNewStackObjectsActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbStopOnNewStackObjectsActionPerformed - - private void cbStopBlockWithZeroActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbStopBlockWithZeroActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbStopBlockWithZeroActionPerformed - private void cbSaveToZipFilesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbSaveToZipFilesActionPerformed // TODO add your handling code here: }//GEN-LAST:event_cbSaveToZipFilesActionPerformed @@ -3349,10 +3082,6 @@ public class PreferencesDialog extends javax.swing.JDialog { } }//GEN-LAST:event_cbUseDefaultImageFolderActionPerformed - private void cbThemeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbThemeActionPerformed - // 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 @@ -3362,14 +3091,6 @@ public class PreferencesDialog extends javax.swing.JDialog { } }//GEN-LAST:event_sliderGUISizeStateChanged - private void cbUseSameSettingsForReplacementEffectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbUseSameSettingsForReplacementEffectActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbUseSameSettingsForReplacementEffectActionPerformed - - private void cbTargetAutoChooseLevelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbTargetAutoChooseLevelActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_cbTargetAutoChooseLevelActionPerformed - private void showProxySettings() { Connection.ProxyType proxyType = (Connection.ProxyType) cbProxyType.getSelectedItem(); switch (proxyType) { @@ -3482,7 +3203,6 @@ public class PreferencesDialog extends javax.swing.JDialog { load(prefs, dialog.displayLifeOnAvatar, KEY_DISPLAY_LIVE_ON_AVATAR, "true"); load(prefs, dialog.showAbilityPickerForced, KEY_SHOW_ABILITY_PICKER_FORCED, "true"); load(prefs, dialog.cbAllowRequestToShowHandCards, KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true"); - load(prefs, dialog.cbShowStormCounter, KEY_GAME_SHOW_STORM_COUNTER, "true"); load(prefs, dialog.cbConfirmEmptyManaPool, KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true"); load(prefs, dialog.cbAskMoveToGraveOrder, KEY_GAME_ASK_MOVE_TO_GRAVE_ORDER, "true"); @@ -4133,7 +3853,6 @@ public class PreferencesDialog extends javax.swing.JDialog { private javax.swing.JComboBox cbPreferredImageLanguage; private javax.swing.JComboBox cbProxyType; private javax.swing.JCheckBox cbSaveToZipFiles; - private javax.swing.JCheckBox cbShowStormCounter; private javax.swing.JCheckBox cbStopAttack; private javax.swing.JCheckBox cbStopBlockWithAny; private javax.swing.JCheckBox cbStopBlockWithZero; diff --git a/Mage.Client/src/main/java/mage/client/dialog/ShowCardsDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ShowCardsDialog.java index abce83a6439..775d18e9bbf 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ShowCardsDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ShowCardsDialog.java @@ -19,7 +19,7 @@ import java.util.UUID; /** - * Game GUI: choose target card from the cards list (example: exile and choose card to cast) + * Game GUI: choose target card from the cards list (example: exile and choose card to cast, choose triggers order, etc) * * @author BetaSteward_at_googlemail.com */ diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java index 0735d589723..36b4e373d49 100644 --- a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java +++ b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java @@ -19,7 +19,6 @@ import javax.swing.Timer; import javax.swing.*; import java.awt.*; - import java.awt.dnd.DragSourceEvent; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; @@ -42,6 +41,20 @@ private Timer countdown; private int timeout; + /** + * ms delay between booster showing up and pick being allowed. + */ + private static final int protectionTime = 1500; + /** + * Timer starting at booster being displayed, to protect from early pick due to clicking + * a little too much on the last pick. + */ + private Timer protectionTimer; + /** + * Number of the latest card pick for which the protection timer has been set. + */ + private int protectionPickNo = 0; + // popup menu area picked cards private final JPopupMenu popupMenuPickedArea; // popup menu for a card @@ -108,6 +121,8 @@ } } ); + + protectionTimer = new Timer(protectionTime, e -> protectionTimer.stop()); } public void cleanUp() { @@ -120,6 +135,13 @@ countdown.removeActionListener(al); } } + + if (protectionTimer != null) { + protectionTimer.stop(); + for (ActionListener al : protectionTimer.getActionListeners()) { + protectionTimer.removeActionListener(al); + } + } } public void changeGUISize() { @@ -311,7 +333,13 @@ } if (!draftBooster.isEmptyGrid()) { - SessionHandler.setBoosterLoaded(draftId); // confirm to the server that the booster has been successfully loaded, otherwise the server will re-send the booster + SessionHandler.setBoosterLoaded(draftId); // confirm to the server that the booster has been successfully loaded, otherwise the server will re-send the booster + + if (pickNo != protectionPickNo && !protectionTimer.isRunning()) { + // Restart the protection timer. + protectionPickNo = pickNo; + protectionTimer.restart(); + } } } @@ -345,6 +373,10 @@ } } + public boolean isAllowedToPick() { + return !protectionTimer.isRunning(); + } + public void hideDraft() { Component c = this.getParent(); while (c != null && !(c instanceof DraftPane)) { @@ -417,7 +449,7 @@ // that's why instead of proactively logging our pick we instead // log *last* pick from the list of picks. // To make this possible we cache the list of cards from the - // previous booster and it's sequence number (pack number / pick number) + // previous booster and its sequence number (pack number / pick number) // in fields currentBooster and currentBoosterHeader. private void logLastPick(DraftPickView pickView) { if (!isLogging()) { @@ -438,11 +470,13 @@ private String getCurrentSetCode() { // TODO: Record set codes for random drafts correctly - if (setCodes.size() >= packNo) { - return setCodes.get(packNo - 1); - } else { - return " "; + if (setCodes != null && setCodes.size() >= packNo) { + String setCode = setCodes.get(packNo - 1); + if (setCode != null) { // Not sure how, but got a NPE from this method P1P2 in a ZEN/ZEN/WWK draft + return setCode; + } } + return " "; } private static boolean isLogging() { @@ -525,7 +559,7 @@ lblPlayer15 = new javax.swing.JLabel(); lblPlayer16 = new javax.swing.JLabel(); draftPicks = new mage.client.cards.CardsList(); - draftBooster = new mage.client.cards.DraftGrid(); + draftBooster = new mage.client.cards.DraftGrid(this); draftLeftPane.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); draftLeftPane.setFocusable(false); diff --git a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java index 13ff20959ac..21872969086 100644 --- a/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/FeedbackPanel.java @@ -38,7 +38,6 @@ public class FeedbackPanel extends javax.swing.JPanel { private FeedbackMode mode; private MageDialog connectedDialog; private ChatPanelBasic connectedChatPanel; - private int lastMessageId; private Map lastOptions = new HashMap<>(); private static final ScheduledExecutorService WORKER = Executors.newSingleThreadScheduledExecutor(); @@ -66,14 +65,8 @@ public class FeedbackPanel extends javax.swing.JPanel { } public void prepareFeedback(FeedbackMode mode, String message, boolean special, Map options, - int messageId, boolean gameNeedUserFeedback, TurnPhase gameTurnPhase) { + boolean gameNeedUserFeedback, TurnPhase gameTurnPhase) { synchronized (this) { - if (messageId < this.lastMessageId) { - // if too many warning messages here then look at GAME_REDRAW_GUI event logic - LOGGER.warn("catch un-synced message from later source (possible reason: connection or performance problems): " + messageId + ", text=" + message); - return; - } - this.lastMessageId = messageId; this.lastOptions = options; this.mode = mode; } 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 1bb24363081..f599c8ff7e7 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -14,6 +14,7 @@ import mage.client.components.KeyboundButton; import mage.client.components.MageComponents; import mage.client.components.ext.dlg.DialogManager; import mage.client.components.layout.RelativeLayout; +import mage.client.components.tray.MageTray; import mage.client.dialog.*; import mage.client.dialog.CardInfoWindowDialog.ShowType; import mage.client.game.FeedbackPanel.FeedbackMode; @@ -122,8 +123,8 @@ public final class GamePanel extends javax.swing.JPanel { private JPopupMenu popupMenuTriggerOrder; // keep game data for updates/re-draws - // warning, it keeps updates from GAME_UPDATE events only and ignore another events with GameView static class LastGameData { + int messageId; GameView game; boolean showPlayable; Map options; @@ -366,7 +367,6 @@ public final class GamePanel extends javax.swing.JPanel { jSplitPane1.setDividerSize(GUISizeHelper.dividerBarSize); jSplitPane2.setDividerSize(GUISizeHelper.dividerBarSize); - txtSpellsCast.setFont(new Font(GUISizeHelper.gameDialogAreaFont.getFontName(), Font.BOLD, GUISizeHelper.gameDialogAreaFont.getSize())); txtHoldPriority.setFont(new Font(GUISizeHelper.gameDialogAreaFont.getFontName(), Font.BOLD, GUISizeHelper.gameDialogAreaFont.getSize())); GUISizeHelper.changePopupMenuFont(popupMenuTriggerOrder); @@ -390,6 +390,7 @@ public final class GamePanel extends javax.swing.JPanel { stackObjects.setPreferredSize(newDimension); stackObjects.setMinimumSize(newDimension); stackObjects.setMaximumSize(newDimension); + stackObjects.changeGUISize(); // must call to cards fit newDimension = new Dimension(newStackWidth, (int) pnlShortCuts.getPreferredSize().getHeight()); pnlShortCuts.setPreferredSize(newDimension); @@ -501,6 +502,10 @@ public final class GamePanel extends javax.swing.JPanel { } else { // play start sound AudioManager.playYourGameStarted(); + if (!AppUtil.isAppActive()) { + MageTray.instance.displayMessage("Your match has started!"); + MageTray.instance.blink(); + } } } @@ -571,7 +576,7 @@ public final class GamePanel extends javax.swing.JPanel { } } - public synchronized void init(GameView game) { + public synchronized void init(int messageId, GameView game) { addPlayers(game); // default menu states setMenuStates( @@ -581,7 +586,7 @@ public final class GamePanel extends javax.swing.JPanel { holdingPriority ); - updateGame(game); + updateGame(messageId, game); } private void addPlayers(GameView game) { @@ -706,12 +711,12 @@ public final class GamePanel extends javax.swing.JPanel { */ } - public synchronized void updateGame(GameView game) { - updateGame(game, false, null, null); + public synchronized void updateGame(int messageId, GameView game) { + updateGame(messageId, game, false, null, null); } - public synchronized void updateGame(GameView game, boolean showPlayable, Map options, Set targets) { - keepLastGameData(game, showPlayable, options, targets); + public synchronized void updateGame(int messageId, GameView game, boolean showPlayable, Map options, Set targets) { + keepLastGameData(messageId, game, showPlayable, options, targets); prepareSelectableView(); updateGame(); } @@ -777,12 +782,6 @@ public final class GamePanel extends javax.swing.JPanel { logger.debug("Step is empty"); this.txtStep.setText(""); } - if (lastGameData.game.getSpellsCastCurrentTurn() > 0 && PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_SHOW_STORM_COUNTER, "true").equals("true")) { - this.txtSpellsCast.setVisible(true); - this.txtSpellsCast.setText(' ' + Integer.toString(lastGameData.game.getSpellsCastCurrentTurn()) + ' '); - } else { - this.txtSpellsCast.setVisible(false); - } this.txtActivePlayer.setText(lastGameData.game.getActivePlayerName()); this.txtPriority.setText(lastGameData.game.getPriorityPlayerName()); @@ -946,6 +945,7 @@ public final class GamePanel extends javax.swing.JPanel { } } + //logger.info("game update, message = " + lastGameData.messageId + ", options = " + lastGameData.options + ", priority = " + lastGameData.game.getPriorityPlayerName()); feedbackPanel.disableUndo(); feedbackPanel.updateOptions(lastGameData.options); @@ -1361,12 +1361,13 @@ public final class GamePanel extends javax.swing.JPanel { windowMap.entrySet().removeIf(entry -> entry.getValue().isClosed()); } - public void ask(String question, GameView gameView, int messageId, Map options) { - updateGame(gameView, false, options, null); - this.feedbackPanel.prepareFeedback(FeedbackMode.QUESTION, question, false, options, messageId, true, gameView.getPhase()); + public void ask(int messageId, GameView gameView, String question, Map options) { + updateGame(messageId, gameView, false, options, null); + this.feedbackPanel.prepareFeedback(FeedbackMode.QUESTION, question, false, options, true, gameView.getPhase()); } - private void keepLastGameData(GameView game, boolean showPlayable, Map options, Set targets) { + private void keepLastGameData(int messageId, GameView game, boolean showPlayable, Map options, Set targets) { + lastGameData.messageId = messageId; lastGameData.game = game; lastGameData.showPlayable = showPlayable; lastGameData.options = options; @@ -1623,8 +1624,8 @@ public final class GamePanel extends javax.swing.JPanel { * @param options * @param messageId */ - public void pickTarget(GameView gameView, Map options, String message, CardsView cardsView, Set targets, boolean required, int messageId) { - updateGame(gameView, false, options, targets); + public void pickTarget(int messageId, GameView gameView, Map options, String message, CardsView cardsView, Set targets, boolean required) { + updateGame(messageId, gameView, false, options, targets); hideAll(); DialogManager.getManager(gameId).fadeOut(); clearPickTargetDialogs(); @@ -1652,28 +1653,28 @@ public final class GamePanel extends javax.swing.JPanel { dialog = prepareCardsDialog(message, cardsView, required, options0, popupMenuType); options0.put("dialog", dialog); } - this.feedbackPanel.prepareFeedback(required ? FeedbackMode.INFORM : FeedbackMode.CANCEL, message, gameView.getSpecial(), options0, messageId, true, gameView.getPhase()); + this.feedbackPanel.prepareFeedback(required ? FeedbackMode.INFORM : FeedbackMode.CANCEL, message, gameView.getSpecial(), options0, true, gameView.getPhase()); if (dialog != null) { this.pickTarget.add(dialog); } } - public void inform(String information, GameView gameView, int messageId) { - updateGame(gameView); - this.feedbackPanel.prepareFeedback(FeedbackMode.INFORM, information, gameView.getSpecial(), null, messageId, false, gameView.getPhase()); + public void inform(int messageId, GameView gameView, String information) { + updateGame(messageId, gameView); + this.feedbackPanel.prepareFeedback(FeedbackMode.INFORM, information, gameView.getSpecial(), null, false, gameView.getPhase()); } - public void endMessage(GameView gameView, Map options, String message, int messageId) { - updateGame(gameView, false, options, null); + public void endMessage(int messageId, GameView gameView, Map options, String message) { + updateGame(messageId, gameView, false, options, null); hideAll(); DialogManager.getManager(gameId).fadeOut(); - this.feedbackPanel.prepareFeedback(FeedbackMode.END, message, false, null, messageId, true, null); + this.feedbackPanel.prepareFeedback(FeedbackMode.END, message, false, null, true, null); ArrowBuilder.getBuilder().removeAllArrows(gameId); } - public void select(GameView gameView, Map options, String message, int messageId) { - updateGame(gameView, true, options, null); + public void select(int messageId, GameView gameView, Map options, String message) { + updateGame(messageId, gameView, true, options, null); hideAll(); DialogManager.getManager(gameId).fadeOut(); @@ -1716,31 +1717,31 @@ public final class GamePanel extends javax.swing.JPanel { priorityPlayerText = " / priority " + gameView.getPriorityPlayerName(); } String messageToDisplay = message + FeedbackPanel.getSmallText(activePlayerText + " / " + gameView.getStep().toString() + priorityPlayerText); - this.feedbackPanel.prepareFeedback(FeedbackMode.SELECT, messageToDisplay, gameView.getSpecial(), panelOptions, messageId, true, gameView.getPhase()); + this.feedbackPanel.prepareFeedback(FeedbackMode.SELECT, messageToDisplay, gameView.getSpecial(), panelOptions, true, gameView.getPhase()); } - public void playMana(GameView gameView, Map options, String message, int messageId) { - updateGame(gameView, true, options, null); + public void playMana(int messageId, GameView gameView, Map options, String message) { + updateGame(messageId, gameView, true, options, null); hideAll(); DialogManager.getManager(gameId).fadeOut(); - this.feedbackPanel.prepareFeedback(FeedbackMode.CANCEL, message, gameView.getSpecial(), options, messageId, true, gameView.getPhase()); + this.feedbackPanel.prepareFeedback(FeedbackMode.CANCEL, message, gameView.getSpecial(), options, true, gameView.getPhase()); } - public void playXMana(GameView gameView, Map options, String message, int messageId) { - updateGame(gameView, true, options, null); + public void playXMana(int messageId, GameView gameView, Map options, String message) { + updateGame(messageId, gameView, true, options, null); hideAll(); DialogManager.getManager(gameId).fadeOut(); - this.feedbackPanel.prepareFeedback(FeedbackMode.CONFIRM, message, gameView.getSpecial(), null, messageId, true, gameView.getPhase()); + this.feedbackPanel.prepareFeedback(FeedbackMode.CONFIRM, message, gameView.getSpecial(), null, true, gameView.getPhase()); } public void replayMessage(String message) { //TODO: implement this } - public void pickAbility(GameView gameView, Map options, AbilityPickerView choices) { - updateGame(gameView, false, options, null); + public void pickAbility(int messageId, GameView gameView, Map options, AbilityPickerView choices) { + updateGame(messageId, gameView, false, options, null); hideAll(); DialogManager.getManager(gameId).fadeOut(); @@ -1764,8 +1765,8 @@ public final class GamePanel extends javax.swing.JPanel { return showCards; } - public void getAmount(GameView gameView, Map options, int min, int max, String message) { - updateGame(gameView, false, options, null); + public void getAmount(int messageId, GameView gameView, Map options, int min, int max, String message) { + updateGame(messageId, gameView, false, options, null); hideAll(); DialogManager.getManager(gameId).fadeOut(); @@ -1777,9 +1778,9 @@ public final class GamePanel extends javax.swing.JPanel { } } - public void getMultiAmount(List messages, GameView gameView, Map options, + public void getMultiAmount(int messageId, GameView gameView, List messages, Map options, int min, int max) { - updateGame(gameView, false, options, null); + updateGame(messageId, gameView, false, options, null); hideAll(); DialogManager.getManager(gameId).fadeOut(); @@ -1787,8 +1788,8 @@ public final class GamePanel extends javax.swing.JPanel { SessionHandler.sendPlayerString(gameId, pickMultiNumber.getMultiAmount()); } - public void getChoice(GameView gameView, Map options, Choice choice, UUID objectId) { - updateGame(gameView, false, options, null); + public void getChoice(int messageId, GameView gameView, Map options, Choice choice, UUID objectId) { + updateGame(messageId, gameView, false, options, null); hideAll(); DialogManager.getManager(gameId).fadeOut(); @@ -1813,8 +1814,8 @@ public final class GamePanel extends javax.swing.JPanel { pickChoice.removeDialog(); } - public void pickPile(GameView gameView, Map options, String message, CardsView pile1, CardsView pile2) { - updateGame(gameView, false, options, null); + public void pickPile(int messageId, GameView gameView, Map options, String message, CardsView pile1, CardsView pile2) { + updateGame(messageId, gameView, false, options, null); hideAll(); DialogManager.getManager(gameId).fadeOut(); @@ -1856,14 +1857,8 @@ public final class GamePanel extends javax.swing.JPanel { lblPriority = new javax.swing.JLabel(); feedbackPanel = new mage.client.game.FeedbackPanel(); - txtSpellsCast = new javax.swing.JLabel(); Border paddingBorder = BorderFactory.createEmptyBorder(4, 4, 4, 4); Border border = BorderFactory.createLineBorder(Color.DARK_GRAY, 2); - txtSpellsCast.setBorder(BorderFactory.createCompoundBorder(border, paddingBorder)); - txtSpellsCast.setBackground(Color.LIGHT_GRAY); - txtSpellsCast.setOpaque(true); - txtSpellsCast.setToolTipText("spells cast during the current turn"); - txtHoldPriority = new javax.swing.JLabel(); txtHoldPriority.setText("Hold"); txtHoldPriority.setBorder(BorderFactory.createCompoundBorder(border, paddingBorder)); @@ -2336,7 +2331,6 @@ public final class GamePanel extends javax.swing.JPanel { ) .addGroup(gl_pnlShortCuts.createSequentialGroup() .addComponent(txtHoldPriority) - .addComponent(txtSpellsCast) /*.addComponent(btnToggleMacro)*/ .addComponent(btnSwitchHands) .addComponent(btnCancelSkip) @@ -2372,7 +2366,6 @@ public final class GamePanel extends javax.swing.JPanel { .addGroup(gl_pnlShortCuts.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) /*.addComponent(btnToggleMacro)*/ .addComponent(txtHoldPriority) - .addComponent(txtSpellsCast) .addComponent(btnSwitchHands) .addComponent(btnCancelSkip) .addComponent(btnConcede) @@ -2808,6 +2801,7 @@ public final class GamePanel extends javax.swing.JPanel { if (!cardViewPopupMenu.getAbility().getRules().isEmpty() && !cardViewPopupMenu.getAbility().getRules().get(0).isEmpty()) { abilityRuleText = cardViewPopupMenu.getAbility().getRules().get(0); + abilityRuleText = abilityRuleText.replace("{this}", cardViewPopupMenu.getName()); } } switch (e.getActionCommand()) { @@ -3001,7 +2995,6 @@ public final class GamePanel extends javax.swing.JPanel { private javax.swing.JSplitPane jSplitPane2; private JPanel jPhases; private JPanel phasesContainer; - private javax.swing.JLabel txtSpellsCast; private javax.swing.JLabel txtHoldPriority; private HoverButton currentStep; diff --git a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java index 034d607f6e0..24e95333b4d 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java @@ -105,12 +105,20 @@ public class PlayerPanelExt extends javax.swing.JPanel { timer.setTaskOnTick(() -> { int priorityTimeValue = pt.getCount() + pt.getBufferCount(); String text = getPriorityTimeLeftString(priorityTimeValue); - + // Set timer text colors (note, if you change it here, change it in update() as well) + Color textColor = null; // use default in HoverButton + Color foregroundColor = Color.BLACK; + if (pt.getBufferCount() > 0) { + textColor = Color.GREEN; + foregroundColor = Color.GREEN.darker().darker(); + } else if (pt.getCount() < 300) { // visual indication for under 5 minutes + textColor = Color.RED; + foregroundColor = Color.RED.darker().darker(); + } PlayerPanelExt.this.avatar.setTopText(text); - PlayerPanelExt.this.avatar.setTopTextColor(pt.getBufferCount() > 0 ? Color.GREEN : null); + PlayerPanelExt.this.avatar.setTopTextColor(textColor); PlayerPanelExt.this.timerLabel.setText(text); - PlayerPanelExt.this.timerLabel - .setForeground(pt.getBufferCount() > 0 ? Color.GREEN.darker().darker() : Color.BLACK); + PlayerPanelExt.this.timerLabel.setForeground(foregroundColor); PlayerPanelExt.this.avatar.repaint(); }); timer.init(gameId); @@ -195,10 +203,10 @@ public class PlayerPanelExt extends javax.swing.JPanel { if (playerLife != pastLife) { if (playerLife > pastLife) { avatar.gainLifeDisplay(); - } else if (playerLife < pastLife) { + } else { avatar.loseLifeDisplay(); } - } else if (playerLife == pastLife) { + } else { avatar.stopLifeDisplay(); } } @@ -311,10 +319,18 @@ public class PlayerPanelExt extends javax.swing.JPanel { this.timer.setBufferCount(player.getBufferTimeLeft()); this.avatar.setTopText(priorityTimeValue); this.timerLabel.setText(priorityTimeValue); - - this.avatar.setTopTextColor(player.getBufferTimeLeft() > 0 ? Color.GREEN : null); - this.timerLabel - .setForeground(player.getBufferTimeLeft() > 0 ? Color.GREEN.darker().darker() : Color.BLACK); + // Set timer text colors (note, if you change it here, change it in init()::timer.setTaskOnTick() as well) + Color textColor = null; // use default in HoverButton + Color foregroundColor = Color.BLACK; + if (player.getBufferTimeLeft() > 0) { + textColor = Color.GREEN; + foregroundColor = Color.GREEN.darker().darker(); + } else if (player.getPriorityTimeLeft() < 300) { // visual indication for under 5 minutes + textColor = Color.RED; + foregroundColor = Color.RED.darker().darker(); + } + this.avatar.setTopTextColor(textColor); + this.timerLabel.setForeground(foregroundColor); } if (player.isTimerActive()) { this.timer.resume(); @@ -360,25 +376,17 @@ public class PlayerPanelExt extends javax.swing.JPanel { private void updateAvatar() { if (flagName == null) { // do only once avatar.setText(this.player.getName()); - if (!player.getUserData().getFlagName().equals(flagName)) { - flagName = player.getUserData().getFlagName(); - this.avatar.setTopTextImage(CountryUtil.getCountryFlagIconSize(flagName, 11).getImage()); - } - // TODO: Add the wins to the tooltiptext of the avatar - String countryname = CountryUtil.getCountryName(flagName); - if (countryname == null) { - countryname = "Unknown"; - } + flagName = player.getUserData().getFlagName(); + this.avatar.setTopTextImage(CountryUtil.getCountryFlagIconSize(flagName, 11).getImage()); + String countryName = CountryUtil.getCountryName(flagName); basicTooltipText = "Name: " + player.getName() - + "
Flag: " + countryname - + "
Constructed rating: " + player.getUserData().getConstructedRating() - + "
Limited rating: " + player.getUserData().getLimitedRating() + + "
Flag: " + (countryName == null ? "Unknown" : countryName) + "
Deck hash code: " + player.getDeckHashCode() - + "
This match wins: " + player.getWins() + " of " + player.getWinsNeeded() + " (to win the match)" - + (player.getUserData() == null ? "" : "
History: " + player.getUserData().getHistory()); + + "
This match wins: " + player.getWins() + " of " + player.getWinsNeeded() + " (to win the match)"; } // Extend tooltip StringBuilder tooltipText = new StringBuilder(basicTooltipText); + tooltipText.append("
Match time remaining: ").append(getPriorityTimeLeftString(player)); this.avatar.setTopTextImageRight(null); for (String name : player.getDesignationNames()) { tooltipText.append("
").append(name); 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 83f750d74ba..ed3bed84271 100644 --- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -16,6 +16,7 @@ import mage.client.util.audio.AudioManager; import mage.client.util.object.SaveObjectUtil; import mage.interfaces.callback.CallbackClient; import mage.interfaces.callback.ClientCallback; +import mage.interfaces.callback.ClientCallbackType; import mage.remote.ActionData; import mage.remote.Session; import mage.view.*; @@ -24,8 +25,7 @@ import org.apache.log4j.Logger; import javax.swing.*; import java.awt.event.KeyEvent; -import java.util.List; -import java.util.UUID; +import java.util.*; /** * @author BetaSteward_at_googlemail.com @@ -34,38 +34,76 @@ public class CallbackClientImpl implements CallbackClient { private static final Logger logger = Logger.getLogger(CallbackClientImpl.class); private final MageFrame frame; - private int messageId = 0; - private int gameInformMessageId = 0; + private final Map lastMessages; public CallbackClientImpl(MageFrame frame) { this.frame = frame; + this.lastMessages = new HashMap<>(); + Arrays.stream(ClientCallbackType.values()).forEach(t -> this.lastMessages.put(t, 0)); } @Override public synchronized void processCallback(final ClientCallback callback) { callback.decompressData(); + + // put replay related code here SaveObjectUtil.saveObject(callback.getData(), callback.getMethod().toString()); + + // all GUI related code must be executed in swing thread SwingUtilities.invokeLater(() -> { try { - logger.debug(callback.getMessageId() + " -- " + callback.getMethod()); + logger.debug("message " + callback.getMessageId() + " - " + callback.getMethod().getType() + " - " + callback.getMethod()); + + // process bad connection (events can income in wrong order, so outdated data must be ignored) + // - table/dialog events like game start, game end, choose dialog - must be processed anyway + // - messages events like chat, inform, error - must be processed anyway + // - update events like opponent priority - can be ignored + if (!callback.getMethod().getType().equals(ClientCallbackType.CLIENT_SIDE_EVENT)) { + int lastAnyMessageId = this.lastMessages.values().stream().mapToInt(x -> x).max().orElse(0); + if (lastAnyMessageId > callback.getMessageId()) { + // un-synced message + if (callback.getMethod().getType().mustIgnoreOnOutdated()) { + // ignore + logger.warn(String.format("ignore un-synced message %d - %s - %s, possible reason: slow connection/performance", + callback.getMessageId(), + callback.getMethod().getType(), + callback.getMethod() + )); + return; + } else { + // process it anyway + logger.debug(String.format("processing un-synced message %d - %s - %s, possible reason: slow connection/performance", + callback.getMessageId(), + callback.getMethod().getType(), + callback.getMethod() + )); + } + } + + // keep track of synced messages only + if (!callback.getMethod().getType().canComeInAnyOrder()) { + this.lastMessages.put(callback.getMethod().getType(), callback.getMessageId()); + } + } + switch (callback.getMethod()) { case START_GAME: { TableClientMessage message = (TableClientMessage) callback.getData(); GameManager.instance.setCurrentPlayerUUID(message.getPlayerId()); - gameStarted(message.getGameId(), message.getPlayerId()); + gameStarted(callback.getMessageId(), message.getGameId(), message.getPlayerId()); break; } case START_TOURNAMENT: { TableClientMessage message = (TableClientMessage) callback.getData(); - tournamentStarted(message.getGameId(), message.getPlayerId()); + tournamentStarted(callback.getMessageId(), message.getGameId(), message.getPlayerId()); break; } case START_DRAFT: { TableClientMessage message = (TableClientMessage) callback.getData(); - draftStarted(message.getGameId(), message.getPlayerId()); + draftStarted(callback.getMessageId(), message.getGameId(), message.getPlayerId()); break; } @@ -149,7 +187,7 @@ public class CallbackClientImpl implements CallbackClient { case REPLAY_INIT: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { - panel.init((GameView) callback.getData()); + panel.init(callback.getMessageId(), (GameView) callback.getData()); } break; } @@ -157,7 +195,7 @@ public class CallbackClientImpl implements CallbackClient { case REPLAY_DONE: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { - panel.endMessage(null, null, (String) callback.getData(), callback.getMessageId()); + panel.endMessage(callback.getMessageId(), null, null, (String) callback.getData()); } break; } @@ -165,7 +203,7 @@ public class CallbackClientImpl implements CallbackClient { case REPLAY_UPDATE: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { - panel.updateGame((GameView) callback.getData()); + panel.updateGame(callback.getMessageId(), (GameView) callback.getData()); } break; } @@ -174,7 +212,7 @@ public class CallbackClientImpl implements CallbackClient { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { appendJsonEvent("GAME_INIT", callback.getObjectId(), callback.getData()); - panel.init((GameView) callback.getData()); + panel.init(callback.getMessageId(), (GameView) callback.getData()); } break; } @@ -190,7 +228,7 @@ public class CallbackClientImpl implements CallbackClient { String logFileName = "game-" + gameId + ".json"; S3Uploader.upload(logFileName, gameId.toString()); } - panel.endMessage(message.getGameView(), message.getOptions(), message.getMessage(), callback.getMessageId()); + panel.endMessage(callback.getMessageId(), message.getGameView(), message.getOptions(), message.getMessage()); } break; } @@ -205,7 +243,7 @@ public class CallbackClientImpl implements CallbackClient { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { appendJsonEvent("GAME_ASK", callback.getObjectId(), message); - panel.ask(message.getMessage(), message.getGameView(), callback.getMessageId(), message.getOptions()); + panel.ask(callback.getMessageId(), message.getGameView(), message.getMessage(), message.getOptions()); } break; } @@ -216,8 +254,8 @@ public class CallbackClientImpl implements CallbackClient { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { appendJsonEvent("GAME_TARGET", callback.getObjectId(), message); - panel.pickTarget(message.getGameView(), message.getOptions(), message.getMessage(), - message.getCardsView1(), message.getTargets(), message.isFlag(), callback.getMessageId()); + panel.pickTarget(callback.getMessageId(), message.getGameView(), message.getOptions(), message.getMessage(), + message.getCardsView1(), message.getTargets(), message.isFlag()); } break; } @@ -227,7 +265,7 @@ public class CallbackClientImpl implements CallbackClient { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { appendJsonEvent("GAME_SELECT", callback.getObjectId(), message); - panel.select(message.getGameView(), message.getOptions(), message.getMessage(), callback.getMessageId()); + panel.select(callback.getMessageId(), message.getGameView(), message.getOptions(), message.getMessage()); } break; } @@ -237,7 +275,7 @@ public class CallbackClientImpl implements CallbackClient { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { appendJsonEvent("GAME_CHOOSE_ABILITY", callback.getObjectId(), callback.getData()); - panel.pickAbility(abilityPickerView.getGameView(), null, abilityPickerView); + panel.pickAbility(callback.getMessageId(), abilityPickerView.getGameView(), null, abilityPickerView); } break; } @@ -247,7 +285,7 @@ public class CallbackClientImpl implements CallbackClient { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { appendJsonEvent("GAME_CHOOSE_PILE", callback.getObjectId(), message); - panel.pickPile(message.getGameView(), message.getOptions(), message.getMessage(), message.getCardsView1(), message.getCardsView2()); + panel.pickPile(callback.getMessageId(), message.getGameView(), message.getOptions(), message.getMessage(), message.getCardsView1(), message.getCardsView2()); } break; } @@ -257,7 +295,7 @@ public class CallbackClientImpl implements CallbackClient { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { appendJsonEvent("GAME_CHOOSE_CHOICE", callback.getObjectId(), message); - panel.getChoice(message.getGameView(), message.getOptions(), message.getChoice(), callback.getObjectId()); + panel.getChoice(callback.getMessageId(), message.getGameView(), message.getOptions(), message.getChoice(), callback.getObjectId()); } break; } @@ -267,7 +305,7 @@ public class CallbackClientImpl implements CallbackClient { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { appendJsonEvent("GAME_PLAY_MANA", callback.getObjectId(), message); - panel.playMana(message.getGameView(), message.getOptions(), message.getMessage(), callback.getMessageId()); + panel.playMana(callback.getMessageId(), message.getGameView(), message.getOptions(), message.getMessage()); } break; } @@ -277,7 +315,7 @@ public class CallbackClientImpl implements CallbackClient { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { appendJsonEvent("GAME_PLAY_XMANA", callback.getObjectId(), message); - panel.playXMana(message.getGameView(), message.getOptions(), message.getMessage(), callback.getMessageId()); + panel.playXMana(callback.getMessageId(), message.getGameView(), message.getOptions(), message.getMessage()); } break; } @@ -287,8 +325,7 @@ public class CallbackClientImpl implements CallbackClient { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { appendJsonEvent("GAME_GET_AMOUNT", callback.getObjectId(), message); - - panel.getAmount(message.getGameView(), message.getOptions(), message.getMin(), message.getMax(), message.getMessage()); + panel.getAmount(callback.getMessageId(), message.getGameView(), message.getOptions(), message.getMin(), message.getMax(), message.getMessage()); } break; } @@ -298,8 +335,7 @@ public class CallbackClientImpl implements CallbackClient { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { appendJsonEvent("GAME_GET_MULTI_AMOUNT", callback.getObjectId(), message); - - panel.getMultiAmount(message.getMessages(), message.getGameView(), message.getOptions(), message.getMin(), message.getMax()); + panel.getMultiAmount(callback.getMessageId(), message.getGameView(), message.getMessages(), message.getOptions(), message.getMin(), message.getMax()); } break; } @@ -308,7 +344,7 @@ public class CallbackClientImpl implements CallbackClient { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { appendJsonEvent("GAME_UPDATE", callback.getObjectId(), callback.getData()); - panel.updateGame((GameView) callback.getData(), true, null, null); // update after undo wtf?! + panel.updateGame(callback.getMessageId(), (GameView) callback.getData(), true, null, null); // update after undo wtf?! // TODO: clean dialogs?! } break; } @@ -336,21 +372,13 @@ public class CallbackClientImpl implements CallbackClient { break; } - case GAME_INFORM: { - if (callback.getMessageId() > gameInformMessageId) { - { - GameClientMessage message = (GameClientMessage) callback.getData(); - GamePanel panel = MageFrame.getGame(callback.getObjectId()); - if (panel != null) { - appendJsonEvent("GAME_INFORM", callback.getObjectId(), message); - panel.inform(message.getMessage(), message.getGameView(), callback.getMessageId()); - } - } - // no longer needed because phase skip handling on server side now - } else { - logger.warn(new StringBuilder("message out of sequence - ignoring").append("MessageId = ").append(callback.getMessageId()).append(" method = ").append(callback.getMethod())); + case GAME_UPDATE_AND_INFORM: { + GameClientMessage message = (GameClientMessage) callback.getData(); + GamePanel panel = MageFrame.getGame(callback.getObjectId()); + if (panel != null) { + appendJsonEvent("GAME_INFORM", callback.getObjectId(), message); + panel.inform(callback.getMessageId(), message.getGameView(), message.getMessage()); } - gameInformMessageId = messageId; break; } @@ -432,14 +460,10 @@ public class CallbackClientImpl implements CallbackClient { } default: { + // TODO: add exception here and process miss events like TOURNAMENT_UPDATE break; } } - - // sync message for server side events only - if (!callback.getMethod().isClientSideMessage()) { - messageId = callback.getMessageId(); - } } catch (Exception ex) { handleException(ex); } @@ -466,50 +490,50 @@ public class CallbackClientImpl implements CallbackClient { switch (usedPanel.getChatType()) { case GAME: usedPanel.receiveMessage("", new StringBuilder() - .append("HOTKEYS:") - .append("
Turn mousewheel up (ALT-e) - enlarge image of card the mousepointer hovers over") - .append("
Turn mousewheel down (ALT-s) - enlarge original/alternate image of card the mousepointer hovers over") - .append("
") - .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_CONFIRM))) - .append(" - Confirm \"Ok\", \"Yes\" or \"Done\" button") - .append("
") - .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_NEXT_TURN))) - .append(" - Skip current turn but stop on declare attackers/blockers and something on the stack") - .append("
") - .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_END_STEP))) - .append(" - Skip to next end step but stop on declare attackers/blockers and something on the stack") - .append("
") - .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_SKIP_STEP))) - .append(" - Skip current turn but stop on declare attackers/blockers") - .append("
") - .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_MAIN_STEP))) - .append(" - Skip to next main phase but stop on declare attackers/blockers and something on the stack") - .append("
") - .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_YOUR_TURN))) - .append(" - Skip everything until your next turn") - .append("
") - .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_PRIOR_END))) - .append(" - Skip everything until the end step just prior to your turn") - .append("
") - .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_CANCEL_SKIP))) - .append(" - Undo F4/F5/F7/F9/F11") - .append("
") - .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_SWITCH_CHAT))) - .append(" - Switch in/out to chat text field") - /* + .append("HOTKEYS:") + .append("
Turn mousewheel up (ALT-e) - enlarge image of card the mousepointer hovers over") + .append("
Turn mousewheel down (ALT-s) - enlarge original/alternate image of card the mousepointer hovers over") .append("
") - .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_TOGGLE_MACRO))) - .append(" - Toggle recording a sequence of actions to repeat. Will not pause if interrupted and can fail if a selected card changes such as when scrying top card to bottom.") - .append("
").append(System.getProperty("os.name").contains("Mac OS X") ? "Cmd" : "Ctrl").append(" + click - Hold priority while casting a spell or activating an ability") - */ - .append("
") - .append("
") - .append("CHAT COMMANDS:") - .append("
").append("/h username - show player's stats (history)") - .append("
").append("/w username message - send private message to player (whisper)") - .append("
").append("/pings - show players and watchers ping") - .append("
").append("/fix - fix frozen game") - .toString(), + .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_CONFIRM))) + .append(" - Confirm \"Ok\", \"Yes\" or \"Done\" button") + .append("
") + .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_NEXT_TURN))) + .append(" - Skip current turn but stop on declare attackers/blockers and something on the stack") + .append("
") + .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_END_STEP))) + .append(" - Skip to next end step but stop on declare attackers/blockers and something on the stack") + .append("
") + .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_SKIP_STEP))) + .append(" - Skip current turn but stop on declare attackers/blockers") + .append("
") + .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_MAIN_STEP))) + .append(" - Skip to next main phase but stop on declare attackers/blockers and something on the stack") + .append("
") + .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_YOUR_TURN))) + .append(" - Skip everything until your next turn") + .append("
") + .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_PRIOR_END))) + .append(" - Skip everything until the end step just prior to your turn") + .append("
") + .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_CANCEL_SKIP))) + .append(" - Undo F4/F5/F7/F9/F11") + .append("
") + .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_SWITCH_CHAT))) + .append(" - Switch in/out to chat text field") + /* + .append("
") + .append(KeyEvent.getKeyText(PreferencesDialog.getCurrentControlKey(PreferencesDialog.KEY_CONTROL_TOGGLE_MACRO))) + .append(" - Toggle recording a sequence of actions to repeat. Will not pause if interrupted and can fail if a selected card changes such as when scrying top card to bottom.") + .append("
").append(System.getProperty("os.name").contains("Mac OS X") ? "Cmd" : "Ctrl").append(" + click - Hold priority while casting a spell or activating an ability") + */ + .append("
") + .append("
") + .append("CHAT COMMANDS:") + .append("
").append("/h username - show player's stats (history)") + .append("
").append("/w username message - send private message to player (whisper)") + .append("
").append("/pings - show players and watchers ping") + .append("
").append("/fix - fix frozen game") + .toString(), null, null, MessageType.USER_INFO, ChatMessage.MessageColor.BLUE); break; case TOURNAMENT: @@ -519,10 +543,10 @@ public class CallbackClientImpl implements CallbackClient { case TABLES: String serverAddress = SessionHandler.getSession().getServerHostname().orElse(""); usedPanel.receiveMessage("", new StringBuilder("Download card images by using the \"Images\" main menu.") - .append("
Download icons and symbols by using the \"Symbols\" main menu.") - .append("
\\list - show a list of available chat commands.") - .append("
").append(IgnoreList.usage(serverAddress)) - .append("
Type \\w yourUserName profanity 0 (or 1 or 2) to turn off/on the profanity filter").toString(), + .append("
Download icons and symbols by using the \"Symbols\" main menu.") + .append("
\\list - show a list of available chat commands.") + .append("
").append(IgnoreList.usage(serverAddress)) + .append("
Type \\w yourUserName profanity 0 (or 1 or 2) to turn off/on the profanity filter").toString(), null, null, MessageType.USER_INFO, ChatMessage.MessageColor.BLUE); break; default: @@ -539,7 +563,7 @@ public class CallbackClientImpl implements CallbackClient { } } - protected void gameStarted(final UUID gameId, final UUID playerId) { + protected void gameStarted(final int messageId, final UUID gameId, final UUID playerId) { try { frame.showGame(gameId, playerId); logger.info("Game " + gameId + " started for player " + playerId); @@ -552,7 +576,7 @@ public class CallbackClientImpl implements CallbackClient { } } - protected void draftStarted(UUID draftId, UUID playerId) { + protected void draftStarted(int messageId, UUID draftId, UUID playerId) { try { frame.showDraft(draftId); logger.info("Draft " + draftId + " started for player " + playerId); @@ -561,7 +585,7 @@ public class CallbackClientImpl implements CallbackClient { } } - protected void tournamentStarted(UUID tournamentId, UUID playerId) { + protected void tournamentStarted(int messageId, UUID tournamentId, UUID playerId) { try { frame.showTournament(tournamentId); AudioManager.playTournamentStarted(); diff --git a/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java index eae1b08bb81..dfa7f117b1f 100644 --- a/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/NewPlayerPanel.java @@ -122,7 +122,7 @@ public class NewPlayerPanel extends javax.swing.JPanel { lblLevel.setText("Skill:"); - spnLevel.setModel(new javax.swing.SpinnerNumberModel(6, 1, 10, 1)); + spnLevel.setModel(new javax.swing.SpinnerNumberModel(2, 1, 10, 1)); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -186,4 +186,4 @@ public class NewPlayerPanel extends javax.swing.JPanel { private javax.swing.JTextField txtPlayerName; // End of variables declaration//GEN-END:variables -} \ No newline at end of file +} 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 c1901322944..2ad921ac7aa 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -99,7 +99,8 @@ public class TablesPanel extends javax.swing.JPanel { + "
FM: = Numbers of freee mulligans" + "
Constr.: = Construction time for limited tournament formats" + "
RB = Rollback allowed" - + "
PC = Planechase active" + + "
PC = Planechase active" + + "
EC = One or more emblem cards in use" + "
SP = Spectators allowed" + "
Rng: Range of visibility for multiplayer matches" ) diff --git a/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java index 9540185bc15..c9b1fc6e6ff 100644 --- a/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java @@ -78,7 +78,7 @@ public class TournamentPlayerPanel extends javax.swing.JPanel { jLabel3.setText("Skill:"); - spnLevel.setModel(new javax.swing.SpinnerNumberModel(6, 1, 10, 1)); + spnLevel.setModel(new javax.swing.SpinnerNumberModel(2, 1, 10, 1)); spnLevel.setRequestFocusEnabled(false); javax.swing.GroupLayout pnlPlayerNameLayout = new javax.swing.GroupLayout(pnlPlayerName); diff --git a/Mage.Client/src/main/java/mage/client/util/object/SaveObjectUtil.java b/Mage.Client/src/main/java/mage/client/util/object/SaveObjectUtil.java index 6f43d8e3f34..809722150f5 100644 --- a/Mage.Client/src/main/java/mage/client/util/object/SaveObjectUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/object/SaveObjectUtil.java @@ -9,6 +9,8 @@ import java.text.SimpleDateFormat; import java.util.Calendar; /** + * TODO: part of replay system? Un-used, can be deleted + * * Utility class to save an object on disk. * * @author ayrat diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java index b8af3144e00..db0aaeecac6 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java @@ -6,14 +6,13 @@ import mage.client.constants.Constants; import mage.constants.Rarity; import org.apache.log4j.Logger; import org.mage.plugins.card.dl.DownloadJob; - -import java.io.File; -import java.util.*; - import static org.mage.plugins.card.dl.DownloadJob.fromURL; import static org.mage.plugins.card.dl.DownloadJob.toFile; import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir; +import java.io.File; +import java.util.*; + /** * WARNING, unsupported images plugin, last updates from 2018 */ @@ -103,7 +102,8 @@ public class GathererSets implements Iterable { "C21","MH2","AFR","AFC","J21","MID","MIC","VOW","VOC","YMID", "NEC","NEO","SNC","NCC","CLB","2X2","DMU","DMC","40K","GN3", "UNF","BRO","BRC","BOT","30A","J22","SCD","DMR","ONE","ONC", - "MOM","MOC","MUL","MAT","LTR","CMM","WOE","WHO","RVR","WOT","WOC" + "MOM","MOC","MUL","MAT","LTR","CMM","WOE","WHO","RVR","WOT", + "WOC","SPG","LCI","LCC","REX" // "HHO", "ANA" -- do not exist on gatherer }; 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 e67b9162b99..91817404a32 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 @@ -533,6 +533,13 @@ public class ScryfallImageSupportCards { add("WOE"); // Wilds of Eldraine add("WOT"); // Wilds of Eldraine: Enchanting Tales add("WOC"); // Wilds of Eldraine Commander + add("LCI"); // Lost Caverns of Ixalan + add("LCC"); // Lost Caverns of Ixalan Commander + add("REX"); // Jurassic World Collection + add("SPG"); // Special Guests + + // Custom sets using Scryfall images - must provide a direct link for each card in directDownloadLinks + add("CALC"); // Custom Alchemized versions of existing cards } }; @@ -999,6 +1006,9 @@ public class ScryfallImageSupportCards { put("PMEI/Jamuraan Lion/10*", "https://api.scryfall.com/cards/pmei/10★/"); // PRES put("PRES/Lathliss, Dragon Queen/149*", "https://api.scryfall.com/cards/pres/149★/"); + + // CALC -- custom alchemy version of cards. + put("CALC/C-Pillar of the Paruns", "https://api.scryfall.com/cards/dis/176/"); } }; diff --git a/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallback.java b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallback.java index 8f20af76c40..4131c2175fe 100644 --- a/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallback.java +++ b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallback.java @@ -2,6 +2,7 @@ package mage.interfaces.callback; import mage.remote.traffic.ZippedObject; import mage.utils.CompressUtil; +import mage.utils.ThreadUtils; import java.io.Serializable; import java.util.UUID; @@ -11,11 +12,23 @@ import java.util.UUID; */ public class ClientCallback implements Serializable { + // for debug only: simulate bad connection on client side, use launcher's client param like -Dxmage.badconnection + private static final String SIMULATE_BAD_CONNECTION_PROP = "xmage.badconnection"; + public static final boolean SIMULATE_BAD_CONNECTION; + + static { + SIMULATE_BAD_CONNECTION = System.getProperty(SIMULATE_BAD_CONNECTION_PROP) != null; + } + private UUID objectId; private Object data; private ClientCallbackMethod method; private int messageId; + public ClientCallback(ClientCallbackMethod method, UUID objectId) { + this(method, objectId, null); + } + public ClientCallback(ClientCallbackMethod method, UUID objectId, Object data) { this(method, objectId, data, true); } @@ -26,8 +39,10 @@ public class ClientCallback implements Serializable { this.setData(data, useCompress); } - public ClientCallback(ClientCallbackMethod method, UUID objectId) { - this(method, objectId, null); + private void simulateBadConnection() { + if (SIMULATE_BAD_CONNECTION) { + ThreadUtils.sleep(100); + } } public void clear() { @@ -55,12 +70,14 @@ public class ClientCallback implements Serializable { this.data = data; } else { this.data = CompressUtil.compress(data); + simulateBadConnection(); } } public void decompressData() { if (this.data instanceof ZippedObject) { this.data = CompressUtil.decompress(this.data); + simulateBadConnection(); } } 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 53ec476e7fe..343ecf52f7b 100644 --- a/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java +++ b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java @@ -1,68 +1,86 @@ package mage.interfaces.callback; /** - * Created by IGOUDT on 4-4-2017. + * Server's commands to process on client side. Commands can come in un-synced state due bad/slow network + *

+ * Can be: + * - critical events (messages, game events, choose dialogs, etc) + * - non-critical events (game updates, messages) */ public enum ClientCallbackMethod { - CHATMESSAGE("chatMessage"), - TOURNAMENT_INIT("tournamentInit"), - TOURNAMENT_UPDATE("tournamentUpdate"), - TOURNAMENT_OVER("tournamentOver"), - JOINED_TABLE("joinedTable"), - START_DRAFT("startDraft"), - START_TOURNAMENT("startTournament"), - SIDEBOARD("sideboard"), - VIEW_LIMITED_DECK("viewLimitedDeck"), - VIEW_SIDEBOARD("viewSideboard"), - CONSTRUCT("construct"), - SHOW_USERMESSAGE("showUserMessage"), - WATCHGAME("watchGame"), - REPLAY_GAME("replayGame"), - START_GAME("startGame"), - SHOW_TOURNAMENT("showTournament"), - SHOW_GAME_END_DIALOG("showGameEndDialog"), - SERVER_MESSAGE("serverMessage"), - GAME_INIT("gameInit"), - GAME_OVER("gameOver"), - GAME_INFORM("gameInform"), - GAME_INFORM_PERSONAL("gameInformPersonal"), - GAME_ERROR("gameError"), - GAME_UPDATE("gameUpdate"), - GAME_REDRAW_GUI("gameRedrawGUI", true), - DRAFT_OVER("draftOver"), - REPLAY_DONE("replayDone"), - USER_REQUEST_DIALOG("userRequestDialog"), - REPLAY_UPDATE("replayUpdate"), - REPLAY_INIT("replayInit"), - END_GAME_INFO("endGameInfo"), - GAME_TARGET("gameTarget"), - GAME_CHOOSE_ABILITY("gameChooseAbility"), - GAME_CHOOSE_PILE("gameChoosePile"), - GAME_CHOOSE_CHOICE("gameChooseChoice"), - GAME_ASK("gameAsk"), - GAME_SELECT("gameSelect"), - 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"); + // TODO: rename events due place/action like GAME_STARTED, GAME_ASK_DIALOG, GAME_TARGET_DIALOG - String code; - boolean isClientSideMessage; + // messages + CHATMESSAGE(ClientCallbackType.MESSAGE, "chatMessage"), + SHOW_USERMESSAGE(ClientCallbackType.MESSAGE, "showUserMessage"), + SERVER_MESSAGE(ClientCallbackType.MESSAGE, "serverMessage"), - ClientCallbackMethod(String code) { - this(code, false); - } + // table + JOINED_TABLE(ClientCallbackType.TABLE_CHANGE, "joinedTable"), - ClientCallbackMethod(String code, boolean isClientSideMessage) { + // tournament + START_TOURNAMENT(ClientCallbackType.TABLE_CHANGE, "startTournament"), + TOURNAMENT_INIT(ClientCallbackType.TABLE_CHANGE, "tournamentInit"), // TODO: unused on client + TOURNAMENT_UPDATE(ClientCallbackType.UPDATE, "tournamentUpdate"), // TODO: unused on client + TOURNAMENT_OVER(ClientCallbackType.TABLE_CHANGE, "tournamentOver"), // TODO: unused on client + + // draft/sideboard + START_DRAFT(ClientCallbackType.TABLE_CHANGE, "startDraft"), + SIDEBOARD(ClientCallbackType.TABLE_CHANGE, "sideboard"), + CONSTRUCT(ClientCallbackType.TABLE_CHANGE, "construct"), + DRAFT_OVER(ClientCallbackType.TABLE_CHANGE, "draftOver"), + DRAFT_INIT(ClientCallbackType.TABLE_CHANGE, "draftInit"), + DRAFT_PICK(ClientCallbackType.TABLE_CHANGE, "draftPick"), + DRAFT_UPDATE(ClientCallbackType.UPDATE, "draftUpdate"), + + // watch + SHOW_TOURNAMENT(ClientCallbackType.TABLE_CHANGE, "showTournament"), + WATCHGAME(ClientCallbackType.TABLE_CHANGE, "watchGame"), + + // in-game actions + VIEW_LIMITED_DECK(ClientCallbackType.MESSAGE, "viewLimitedDeck"), + VIEW_SIDEBOARD(ClientCallbackType.MESSAGE, "viewSideboard"), + + // other + USER_REQUEST_DIALOG(ClientCallbackType.DIALOG, "userRequestDialog"), + GAME_REDRAW_GUI(ClientCallbackType.CLIENT_SIDE_EVENT, "gameRedrawGUI"), + + // game + START_GAME(ClientCallbackType.TABLE_CHANGE, "startGame"), + GAME_INIT(ClientCallbackType.TABLE_CHANGE, "gameInit"), + GAME_UPDATE_AND_INFORM(ClientCallbackType.UPDATE, "gameInform"), // update game and feedback panel with current status (e.g. on non our priority) + GAME_INFORM_PERSONAL(ClientCallbackType.MESSAGE, "gameInformPersonal"), + GAME_ERROR(ClientCallbackType.MESSAGE, "gameError"), + GAME_UPDATE(ClientCallbackType.UPDATE, "gameUpdate"), + GAME_TARGET(ClientCallbackType.DIALOG, "gameTarget"), + GAME_CHOOSE_ABILITY(ClientCallbackType.DIALOG, "gameChooseAbility"), + GAME_CHOOSE_PILE(ClientCallbackType.DIALOG, "gameChoosePile"), + GAME_CHOOSE_CHOICE(ClientCallbackType.DIALOG, "gameChooseChoice"), + GAME_ASK(ClientCallbackType.DIALOG, "gameAsk"), + GAME_SELECT(ClientCallbackType.DIALOG, "gameSelect"), + GAME_PLAY_MANA(ClientCallbackType.DIALOG, "gamePlayMana"), + GAME_PLAY_XMANA(ClientCallbackType.DIALOG, "gamePlayXMana"), + GAME_GET_AMOUNT(ClientCallbackType.DIALOG, "gameSelectAmount"), + GAME_GET_MULTI_AMOUNT(ClientCallbackType.DIALOG, "gameSelectMultiAmount"), + GAME_OVER(ClientCallbackType.TABLE_CHANGE, "gameOver"), + END_GAME_INFO(ClientCallbackType.TABLE_CHANGE, "endGameInfo"), + + // replay (unsupported) + REPLAY_GAME(ClientCallbackType.TABLE_CHANGE, "replayGame"), + REPLAY_INIT(ClientCallbackType.TABLE_CHANGE, "replayInit"), + REPLAY_UPDATE(ClientCallbackType.UPDATE, "replayUpdate"), + REPLAY_DONE(ClientCallbackType.TABLE_CHANGE, "replayDone"); + + final ClientCallbackType type; + final String code; + + ClientCallbackMethod(ClientCallbackType type, String code) { + this.type = type; this.code = code; - this.isClientSideMessage = isClientSideMessage; } - public boolean isClientSideMessage() { - return this.isClientSideMessage; + public ClientCallbackType getType() { + return this.type; } } diff --git a/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackType.java b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackType.java new file mode 100644 index 00000000000..61636256484 --- /dev/null +++ b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackType.java @@ -0,0 +1,35 @@ +package mage.interfaces.callback; + +/** + * Server event type for processing on the client + * + * @author JayDi85 + */ +public enum ClientCallbackType { + + UPDATE(true, true), // game update + TABLE_CHANGE, // all important game events + game update + MESSAGE(true, false), // show message/log without game update + DIALOG, // all dialogs + game update + CLIENT_SIDE_EVENT(true, true); // without game uodate + + final boolean canComeInAnyOrder; + final boolean mustIgnoreOnOutdated; // if event come in any order and contain game update then it must be ignored on outdate + + ClientCallbackType() { + this(false, false); + } + + ClientCallbackType(boolean canComeInAnyOrder, boolean mustIgnoreOnOutdated) { + this.canComeInAnyOrder = canComeInAnyOrder; + this.mustIgnoreOnOutdated = mustIgnoreOnOutdated; + } + + public boolean canComeInAnyOrder() { + return this.canComeInAnyOrder; + } + + public boolean mustIgnoreOnOutdated() { + return this.mustIgnoreOnOutdated; + } +} diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index 0112f3f13fd..6ec3569fabf 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -716,8 +716,9 @@ public class CardView extends SimpleCardView { // emblem images are always with common (black) symbol this.frameStyle = FrameStyle.M15_NORMAL; this.expansionSetCode = emblem.getExpansionSetCode(); - this.cardNumber = ""; + this.cardNumber = emblem.getCardNumber(); this.imageNumber = emblem.getImageNumber(); + this.usesVariousArt = emblem.getUsesVariousArt(); this.rarity = Rarity.COMMON; this.playableStats = emblem.playableStats.copy(); diff --git a/Mage.Common/src/main/java/mage/view/EmblemView.java b/Mage.Common/src/main/java/mage/view/EmblemView.java index 58dc7c89dbe..23b3768e967 100644 --- a/Mage.Common/src/main/java/mage/view/EmblemView.java +++ b/Mage.Common/src/main/java/mage/view/EmblemView.java @@ -1,7 +1,7 @@ package mage.view; -import mage.cards.Card; import mage.game.command.Emblem; +import mage.game.command.emblems.EmblemOfCard; import mage.players.PlayableObjectStats; import java.io.Serializable; @@ -15,7 +15,9 @@ public class EmblemView implements CommandObjectView, Serializable { protected UUID id; protected String name; + protected String cardNumber = ""; protected int imageNum; + protected boolean usesVariousArt = false; protected String expansionSetCode; protected List rules; protected PlayableObjectStats playableStats = new PlayableObjectStats(); @@ -26,6 +28,10 @@ public class EmblemView implements CommandObjectView, Serializable { this.imageNum = emblem.getImageNumber(); this.expansionSetCode = emblem.getExpansionSetCode(); this.rules = emblem.getAbilities().getRules(emblem.getName()); + if (emblem instanceof EmblemOfCard) { + cardNumber = emblem.getCardNumber(); + usesVariousArt = ((EmblemOfCard) emblem).getUsesVariousArt(); + } } @Override @@ -43,10 +49,17 @@ public class EmblemView implements CommandObjectView, Serializable { return id; } + public String getCardNumber() { + return cardNumber; + } + @Override public int getImageNumber() { return imageNum; } + public boolean getUsesVariousArt() { + return this.usesVariousArt; + } @Override public List getRules() { diff --git a/Mage.Common/src/main/java/mage/view/GameView.java b/Mage.Common/src/main/java/mage/view/GameView.java index 91529f791a5..cce1115a643 100644 --- a/Mage.Common/src/main/java/mage/view/GameView.java +++ b/Mage.Common/src/main/java/mage/view/GameView.java @@ -62,7 +62,6 @@ public class GameView implements Serializable { private final int turn; private boolean special = false; private final boolean isPlayer; // false = watching user - private final int spellsCastCurrentTurn; private final boolean rollbackTurnsAllowed; public GameView(GameState state, Game game, UUID createdForPlayerId, UUID watcherUserId) { @@ -195,13 +194,6 @@ public class GameView implements Serializable { } else { this.special = false; } - - CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); - if (watcher != null) { - spellsCastCurrentTurn = watcher.getAmountOfSpellsAllPlayersCastOnCurrentTurn(); - } else { - spellsCastCurrentTurn = 0; - } rollbackTurnsAllowed = game.getOptions().rollbackTurnsAllowed; } @@ -335,10 +327,6 @@ public class GameView implements Serializable { this.canPlayObjects = canPlayObjects; } - public int getSpellsCastCurrentTurn() { - return spellsCastCurrentTurn; - } - public boolean isRollbackTurnsAllowed() { return rollbackTurnsAllowed; } diff --git a/Mage.Common/src/main/java/mage/view/TableView.java b/Mage.Common/src/main/java/mage/view/TableView.java index 0c016a1e84e..a4a9c9658f0 100644 --- a/Mage.Common/src/main/java/mage/view/TableView.java +++ b/Mage.Common/src/main/java/mage/view/TableView.java @@ -117,6 +117,10 @@ public class TableView implements Serializable { if (table.getMatch().getOptions().isPlaneChase()) { addInfo.append(" PC"); } + if (!(table.getMatch().getOptions().getPerPlayerEmblemCards().isEmpty()) + || !(table.getMatch().getOptions().getGlobalEmblemCards().isEmpty())) { + addInfo.append(" EC"); + } if (table.getMatch().getOptions().isSpectatorsAllowed()) { addInfo.append(" SP"); } @@ -177,6 +181,10 @@ public class TableView implements Serializable { if (tourneyMatchOptions.isPlaneChase()) { infoText.append(" PC"); } + if (!(table.getTournament().getOptions().getMatchOptions().getPerPlayerEmblemCards().isEmpty()) + || !(table.getTournament().getOptions().getMatchOptions().getGlobalEmblemCards().isEmpty())) { + infoText.append(" EC"); + } if (table.getTournament().getOptions().isWatchingAllowed()) { infoText.append(" SP"); } 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 d0a4d199a49..a5b0212296f 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 @@ -68,7 +68,7 @@ public class Modern extends Constructed { banned.add("Tree of Tales"); banned.add("Umezawa's Jitte"); banned.add("Uro, Titan of Nature's Wrath"); - banned.add("Yorion, the Sky Nomad"); + banned.add("Yorion, Sky Nomad"); banned.add("Vault of Whispers"); } } diff --git a/Mage.Server.Plugins/Mage.Game.CustomPillarOfTheParunsDuel/src/mage/game/CustomPillarOfTheParunsDuel.java b/Mage.Server.Plugins/Mage.Game.CustomPillarOfTheParunsDuel/src/mage/game/CustomPillarOfTheParunsDuel.java index da43968c5ec..de170dfe6c9 100644 --- a/Mage.Server.Plugins/Mage.Game.CustomPillarOfTheParunsDuel/src/mage/game/CustomPillarOfTheParunsDuel.java +++ b/Mage.Server.Plugins/Mage.Game.CustomPillarOfTheParunsDuel/src/mage/game/CustomPillarOfTheParunsDuel.java @@ -32,9 +32,10 @@ import java.util.UUID; * To summarize, this uses the default rules for a 1v1 limited match, * with two additional custom rules:

* -> At the beginning of each player's first main phase, that player - * conjure into play a Pillar of the Paruns. This does count as a - * land drop for the turn.

- * -> The starting hand size is 6, not 7. + * conjure into play a custom version of Pillar of the Paruns. This + * does count as a land drop for the turn. The custom Pillar has + * hexproof and gain "{T}: add {1}."

+ * -> The starting hand size is 6, and the starting life count is 25. *

* I did took the inspiration for the mode from this cube list (not * sure it is the original source for the idea, but i did not found @@ -49,7 +50,7 @@ import java.util.UUID; public class CustomPillarOfTheParunsDuel extends GameImpl { public CustomPillarOfTheParunsDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan) { - super(attackOption, range, mulligan, 20, 40, 6); + super(attackOption, range, mulligan, 25, 40, 6); } @Override @@ -57,7 +58,10 @@ public class CustomPillarOfTheParunsDuel extends GameImpl { super.init(choosingPlayerId); getPlayers().forEach((playerId, p) -> { - addDelayedTriggeredAbility(new AtTheBeginOfPlayerFirstMainPhase(playerId, "Pillar of the Paruns"), null); + addDelayedTriggeredAbility( + new AtTheBeginOfPlayerFirstMainPhase(playerId, "C-Pillar of the Paruns"), + null // TODO: Not sure how to mock something to be displayed instead. + ); }); state.getTurnMods().add(new TurnMod(startingPlayerId).withSkipStep(PhaseStep.DRAW)); @@ -89,28 +93,28 @@ class InitPillarOfTheParunsEffect extends OneShotEffect { private UUID playerId; private String cardName; - InitPillarOfTheParunsEffect(UUID playerId, String cardName){ + InitPillarOfTheParunsEffect(UUID playerId, String cardName) { super(Outcome.PutLandInPlay); this.playerId = playerId; this.cardName = cardName; this.staticText = "conjure " + cardName + " in play. It does count as a land played for the turn."; } - private InitPillarOfTheParunsEffect(final InitPillarOfTheParunsEffect effect){ + private InitPillarOfTheParunsEffect(final InitPillarOfTheParunsEffect effect) { super(effect); this.playerId = effect.playerId; this.cardName = effect.cardName; } @Override - public InitPillarOfTheParunsEffect copy(){ + public InitPillarOfTheParunsEffect copy() { return new InitPillarOfTheParunsEffect(this); } @Override - public boolean apply(Game game, Ability source){ + public boolean apply(Game game, Ability source) { Player player = game.getPlayer(playerId); - if(player == null){ + if (player == null) { return false; } 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 833fcf74447..2a5c90aaeae 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 @@ -61,6 +61,8 @@ public class HumanPlayer extends PlayerImpl { private static final boolean ALLOW_USERS_TO_PUT_NON_PLAYABLE_SPELLS_ON_STACK_WORKAROUND = false; // warning, see workaround's info on usage + // TODO: all user feedback actions executed and waited in diff threads and can't catch exeptions, e.g. on wrong code usage + // must catch and log such errors private transient Boolean responseOpenedForAnswer = false; // can't get response until prepared target (e.g. until send all fire events to all players) private final transient PlayerResponse response = new PlayerResponse(); @@ -1315,7 +1317,7 @@ public class HumanPlayer extends PlayerImpl { while (canRespond()) { // try to set trigger auto order java.util.List abilitiesWithNoOrderSet = new ArrayList<>(); - TriggeredAbility abilityOrderLast = null; + java.util.List abilitiesOrderLast = new ArrayList<>(); for (TriggeredAbility ability : abilities) { if (triggerAutoOrderAbilityFirst.contains(ability.getOriginalId())) { return ability; @@ -1326,17 +1328,20 @@ public class HumanPlayer extends PlayerImpl { return ability; } if (triggerAutoOrderAbilityLast.contains(ability.getOriginalId())) { - abilityOrderLast = ability; + // multiple instances of same trigger has same originalId, no need to select order for it + abilitiesOrderLast.add(ability); continue; } if (triggerAutoOrderNameLast.contains(rule)) { - abilityOrderLast = ability; + abilitiesOrderLast.add(ability); continue; } if (autoOrderUse) { + // multiple triggers with same rule text will be auto-ordered if (autoOrderRuleText == null) { autoOrderRuleText = rule; } else if (!rule.equals(autoOrderRuleText)) { + // diff triggers, so must use choose dialog autoOrderUse = false; } } @@ -1344,7 +1349,8 @@ public class HumanPlayer extends PlayerImpl { } if (abilitiesWithNoOrderSet.isEmpty()) { - return abilityOrderLast; + // user can send diff abilities to the last, will be selected by "first" like first ordered ability above + return abilitiesOrderLast.stream().findFirst().orElse(null); } if (abilitiesWithNoOrderSet.size() == 1 @@ -1352,6 +1358,21 @@ public class HumanPlayer extends PlayerImpl { return abilitiesWithNoOrderSet.iterator().next(); } + // runtime check: lost triggers for GUI + List processingAbilities = new ArrayList<>(abilitiesWithNoOrderSet); + processingAbilities.addAll(abilitiesOrderLast); + + if (abilities.size() != processingAbilities.size()) { + throw new IllegalStateException(String.format("Choose dialog lost some of the triggered abilities:\n" + + "Must %d:\n%s\n" + + "Has %d:\n%s", + abilities.size(), + abilities.stream().map(Ability::getRule).collect(Collectors.joining("\n")), + processingAbilities.size(), + processingAbilities.stream().map(Ability::getRule).collect(Collectors.joining("\n")) + )); + } + macroTriggeredSelectionFlag = true; updateGameStatePriority("chooseTriggeredAbility", game); prepareForResponse(game); @@ -2662,6 +2683,13 @@ public class HumanPlayer extends PlayerImpl { } } + /** + * GUI related, remember choices for choose trigger dialog + * + * @param playerAction + * @param game + * @param data + */ private void setTriggerAutoOrder(PlayerAction playerAction, Game game, Object data) { if (playerAction == TRIGGER_AUTO_ORDER_RESET_ALL) { triggerAutoOrderAbilityFirst.clear(); @@ -2670,7 +2698,9 @@ public class HumanPlayer extends PlayerImpl { triggerAutoOrderNameLast.clear(); return; } + if (data instanceof UUID) { + // remember by id UUID abilityId = (UUID) data; UUID originalId = null; for (TriggeredAbility ability : game.getState().getTriggered(getId())) { @@ -2685,12 +2715,17 @@ public class HumanPlayer extends PlayerImpl { triggerAutoOrderAbilityFirst.add(originalId); break; case TRIGGER_AUTO_ORDER_ABILITY_LAST: - triggerAutoOrderAbilityFirst.add(originalId); + triggerAutoOrderAbilityLast.add(originalId); break; } } } else if (data instanceof String) { + // remember by name String abilityName = (String) data; + if (abilityName.contains("{this}")) { + throw new IllegalArgumentException("Wrong code usage. Remembering trigger must contains full rules name without {this}."); + } + switch (playerAction) { case TRIGGER_AUTO_ORDER_NAME_FIRST: triggerAutoOrderNameFirst.add(abilityName); diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index 3d91adbff26..7468a1950fd 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -628,6 +628,8 @@ public class TableController { gameOptions.rollbackTurnsAllowed = match.getOptions().isRollbackTurnsAllowed(); gameOptions.bannedUsers = match.getOptions().getBannedUsers(); gameOptions.planeChase = match.getOptions().isPlaneChase(); + gameOptions.perPlayerEmblemCards = match.getOptions().getPerPlayerEmblemCards(); + gameOptions.globalEmblemCards = match.getOptions().getGlobalEmblemCards(); match.getGame().setGameOptions(gameOptions); managerFactory.gameManager().createGameSession(match.getGame(), userPlayerMap, table.getId(), choosingPlayerId, gameOptions); String creator = null; diff --git a/Mage.Server/src/main/java/mage/server/User.java b/Mage.Server/src/main/java/mage/server/User.java index 41e0378c96b..05481e7c69c 100644 --- a/Mage.Server/src/main/java/mage/server/User.java +++ b/Mage.Server/src/main/java/mage/server/User.java @@ -271,10 +271,6 @@ public class User { fireCallback(new ClientCallback(ClientCallbackMethod.SHOW_TOURNAMENT, tournamentId)); } - public void ccShowGameEndDialog(final UUID gameId) { - fireCallback(new ClientCallback(ClientCallbackMethod.SHOW_GAME_END_DIALOG, gameId)); - } - public void showUserMessage(final String titel, String message) { List messageData = new LinkedList<>(); messageData.add(titel); diff --git a/Mage.Server/src/main/java/mage/server/game/GameSessionWatcher.java b/Mage.Server/src/main/java/mage/server/game/GameSessionWatcher.java index 452e948abe3..12c562f9c17 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameSessionWatcher.java +++ b/Mage.Server/src/main/java/mage/server/game/GameSessionWatcher.java @@ -58,7 +58,7 @@ public class GameSessionWatcher { public void inform(final String message) { if (!killed) { - userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_INFORM, game.getId(), new GameClientMessage(getGameView(), null, message)))); + userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_UPDATE_AND_INFORM, game.getId(), new GameClientMessage(getGameView(), null, message)))); } } diff --git a/Mage.Sets/src/mage/cards/a/Abeyance.java b/Mage.Sets/src/mage/cards/a/Abeyance.java index 3e35499f1ce..f467490ab49 100644 --- a/Mage.Sets/src/mage/cards/a/Abeyance.java +++ b/Mage.Sets/src/mage/cards/a/Abeyance.java @@ -61,11 +61,6 @@ class AbeyanceEffect extends ContinuousRuleModifyingEffectImpl { return new AbeyanceEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -76,6 +71,12 @@ class AbeyanceEffect extends ContinuousRuleModifyingEffectImpl { return null; } + @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 (source.getFirstTarget() != null diff --git a/Mage.Sets/src/mage/cards/a/AetherStorm.java b/Mage.Sets/src/mage/cards/a/AetherStorm.java index 4dc9a501106..bcadf719de2 100644 --- a/Mage.Sets/src/mage/cards/a/AetherStorm.java +++ b/Mage.Sets/src/mage/cards/a/AetherStorm.java @@ -57,11 +57,6 @@ class AetherStormReplacementEffect extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public AetherStormReplacementEffect copy() { return new AetherStormReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/a/AggressiveMining.java b/Mage.Sets/src/mage/cards/a/AggressiveMining.java index 3da112d53bf..f4ffe419c9d 100644 --- a/Mage.Sets/src/mage/cards/a/AggressiveMining.java +++ b/Mage.Sets/src/mage/cards/a/AggressiveMining.java @@ -64,11 +64,6 @@ class AggressiveMiningEffect extends ContinuousRuleModifyingEffectImpl { public AggressiveMiningEffect copy() { return new AggressiveMiningEffect(this); } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } @Override public boolean checksEventType(GameEvent event, Game game) { diff --git a/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java b/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java index 994590006af..282cb292d6b 100644 --- a/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java +++ b/Mage.Sets/src/mage/cards/a/AgrusKosEternalSoldier.java @@ -19,9 +19,11 @@ import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; 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.util.CardUtil; import java.util.Collection; import java.util.List; @@ -88,11 +90,14 @@ class AgrusKosEternalSoldierTriggeredAbility extends TriggeredAbilityImpl { if (!event.getTargetId().equals(getSourceId())) { return false; } - StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); - if (stackObject == null) { + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || targetingObject instanceof Spell) { return false; } - Set targets = stackObject + if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) { + return false; + } + Set targets = targetingObject .getStackAbility() .getTargets() .stream() @@ -102,7 +107,7 @@ class AgrusKosEternalSoldierTriggeredAbility extends TriggeredAbilityImpl { if (targets.isEmpty() || !targets.stream().allMatch(getSourceId()::equals)) { return false; } - this.getEffects().setValue("triggeringAbility", stackObject); + this.getEffects().setValue("triggeringAbility", targetingObject); return true; } } diff --git a/Mage.Sets/src/mage/cards/a/AllegiantGeneralPryde.java b/Mage.Sets/src/mage/cards/a/AllegiantGeneralPryde.java index 8e4794cc58a..4ba49612b43 100644 --- a/Mage.Sets/src/mage/cards/a/AllegiantGeneralPryde.java +++ b/Mage.Sets/src/mage/cards/a/AllegiantGeneralPryde.java @@ -1,18 +1,19 @@ package mage.cards.a; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; -import mage.abilities.effects.common.SacrificeEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; @@ -36,21 +37,12 @@ public class AllegiantGeneralPryde extends CardImpl { this.toughness = new MageInt(2); // Trooper creatures you control have "When this creature enters the battlefield, you may sacrifice a creature. If you do, draw two cards and lose 2 life." - SacrificeEffect sacrifceEffect = new SacrificeEffect( - StaticFilters.FILTER_PERMANENT_CREATURE_CONTROLLED, 1, ""); - EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(sacrifceEffect, true, true); - ability.setTriggerPhrase("When this creature enters the battlefield, "); - ability.addTarget(new TargetControlledCreaturePermanent(1)); - DrawCardSourceControllerEffect drawCardSourceControllerEffect = new DrawCardSourceControllerEffect(2); - drawCardSourceControllerEffect.setText("If you do, draw two cards"); - ability.addEffect(drawCardSourceControllerEffect); - LoseLifeSourceControllerEffect loseLifeSourceControllerEffect = new LoseLifeSourceControllerEffect(2); - loseLifeSourceControllerEffect.setText("and lose 2 life."); - ability.addEffect(loseLifeSourceControllerEffect); - //EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(2), true); - GainAbilityControlledEffect effect = new GainAbilityControlledEffect( - ability, Duration.WhileOnBattlefield, filter); - this.addAbility(new SimpleStaticAbility(effect)); + Ability gainedAbility = new EntersBattlefieldTriggeredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(2), + new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + ).addEffect(new LoseLifeSourceControllerEffect(2).concatBy("and"))) + .setTriggerPhrase("When this creature enters the battlefield, "); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(gainedAbility, Duration.WhileOnBattlefield, filter))); } private AllegiantGeneralPryde(final AllegiantGeneralPryde card) { diff --git a/Mage.Sets/src/mage/cards/a/AmuletOfSafekeeping.java b/Mage.Sets/src/mage/cards/a/AmuletOfSafekeeping.java index 609f18f196a..fc149d9c60e 100644 --- a/Mage.Sets/src/mage/cards/a/AmuletOfSafekeeping.java +++ b/Mage.Sets/src/mage/cards/a/AmuletOfSafekeeping.java @@ -1,7 +1,7 @@ package mage.cards.a; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.common.TargetOfOpponentsSpellOrAbilityTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; @@ -9,6 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.StaticFilters; @@ -24,7 +25,8 @@ public final class AmuletOfSafekeeping extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Whenever you become the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {1}. - this.addAbility(new TargetOfOpponentsSpellOrAbilityTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(1)), false, true)); + this.addAbility(new BecomesTargetControllerTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(1)), + null, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); // Creature tokens get -1/-0. this.addAbility(new SimpleStaticAbility( diff --git a/Mage.Sets/src/mage/cards/a/AncientLumberknot.java b/Mage.Sets/src/mage/cards/a/AncientLumberknot.java index 31507014fc7..8d8224ae9a6 100644 --- a/Mage.Sets/src/mage/cards/a/AncientLumberknot.java +++ b/Mage.Sets/src/mage/cards/a/AncientLumberknot.java @@ -2,12 +2,11 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessAllEffect; +import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicate; import mage.game.Game; @@ -21,11 +20,10 @@ import java.util.UUID; public final class AncientLumberknot extends CardImpl { private static final FilterCreaturePermanent filter - = new FilterCreaturePermanent("each creature you control with toughness greater than its power"); + = new FilterCreaturePermanent("creature you control with toughness greater than its power"); static { filter.add(AncientLumberknotPredicate.instance); - filter.add(TargetController.YOU.getControllerPredicate()); } public AncientLumberknot(UUID ownerId, CardSetInfo setInfo) { @@ -36,7 +34,7 @@ public final class AncientLumberknot extends CardImpl { this.toughness = new MageInt(4); // Each creature you control with toughness greater than its power assigns combat damage equal to its toughness rather than its power. - this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessAllEffect(filter))); + this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessControlledEffect(filter))); } private AncientLumberknot(final AncientLumberknot card) { diff --git a/Mage.Sets/src/mage/cards/a/AngelicArbiter.java b/Mage.Sets/src/mage/cards/a/AngelicArbiter.java index f903d4c8e5c..6517f6c4df6 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicArbiter.java +++ b/Mage.Sets/src/mage/cards/a/AngelicArbiter.java @@ -98,11 +98,6 @@ class AngelicArbiterEffect2 extends ContinuousRuleModifyingEffectImpl { return new AngelicArbiterEffect2(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage.Sets/src/mage/cards/a/AngelicCub.java b/Mage.Sets/src/mage/cards/a/AngelicCub.java index 460320f9aaa..f74be87e0d6 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicCub.java +++ b/Mage.Sets/src/mage/cards/a/AngelicCub.java @@ -1,7 +1,7 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -12,18 +12,15 @@ 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.constants.Zone; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher; +import mage.filter.StaticFilters; import java.util.UUID; /** - * @author AustinYQM + * @author AustinYQM, xenohedron */ public final class AngelicCub extends CardImpl { @@ -36,7 +33,11 @@ public final class AngelicCub extends CardImpl { this.toughness = new MageInt(1); // Whenever Angelic Cub becomes the target of a spell or ability for the first time each turn, put a +1/+1 counter on it. - this.addAbility(new AngelicCubAbility(), new NumberOfTimesPermanentTargetedATurnWatcher()); + this.addAbility(new BecomesTargetSourceFirstTimeTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on it"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.NONE, false + )); // As long as Angelic Cub has three or more +1/+1 counters on it, it has flying. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield), new SourceHasCounterCondition(CounterType.P1P1, 3), "As long as {this} has three or more +1/+1 counters on it, it has flying."))); } @@ -50,41 +51,3 @@ public final class AngelicCub extends CardImpl { return new AngelicCub(this); } } - -class AngelicCubAbility extends TriggeredAbilityImpl { - - public AngelicCubAbility() { - super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false); - } - - public AngelicCubAbility(final mage.cards.a.AngelicCubAbility ability) { - super(ability); - } - - @Override - public mage.cards.a.AngelicCubAbility copy() { - return new mage.cards.a.AngelicCubAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature(game)) { - NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); - return watcher != null && watcher.notMoreThanOnceTargetedThisTurn(permanent, game); - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability for the first time each turn, put a +1/+1 counter on it."; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AngelicProtector.java b/Mage.Sets/src/mage/cards/a/AngelicProtector.java index ac598d01cca..ba108214855 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicProtector.java +++ b/Mage.Sets/src/mage/cards/a/AngelicProtector.java @@ -1,7 +1,7 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -25,9 +25,9 @@ public final class AngelicProtector extends CardImpl { this.toughness = new MageInt(2); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new BoostSourceEffect(0, 3, Duration.EndOfTurn) - ).setTriggerPhrase("Whenever {this} becomes the target of a spell or ability, ")); + ).withRuleTextReplacement(false)); } private AngelicProtector(final AngelicProtector card) { diff --git a/Mage.Sets/src/mage/cards/a/ApocalypseChime.java b/Mage.Sets/src/mage/cards/a/ApocalypseChime.java index 48a756dc133..10e9d8580e5 100644 --- a/Mage.Sets/src/mage/cards/a/ApocalypseChime.java +++ b/Mage.Sets/src/mage/cards/a/ApocalypseChime.java @@ -1,6 +1,8 @@ - package mage.cards.a; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -14,7 +16,7 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.card.ExpansionSetPredicate; +import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.permanent.TokenPredicate; /** @@ -26,10 +28,14 @@ public final class ApocalypseChime extends CardImpl { private static final FilterPermanent filter = new FilterPermanent("nontoken permanents with a name originally printed in the Homelands expansion"); static { - filter.add(Predicates.and( - TokenPredicate.FALSE, - new ExpansionSetPredicate("HML") - )); + // Homelands names per CR 206.3c + List nameStrings = Arrays.asList("Abbey Gargoyles", "Abbey Matron", "Aether Storm", "Aliban's Tower", "Ambush", "Ambush Party", "Anaba Ancestor", "Anaba Bodyguard", "Anaba Shaman", "Anaba Spirit Crafter", "An-Havva Constable", "An-Havva Inn", "An-Havva Township", "An-Zerrin Ruins", "Apocalypse Chime", "Autumn Willow", "Aysen Abbey", "Aysen Bureaucrats", "Aysen Crusader", "Aysen Highway", "Baki's Curse", "Baron Sengir", "Beast Walkers", "Black Carriage", "Broken Visage", "Carapace", "Castle Sengir", "Cemetery Gate", "Chain Stasis", "Chandler", "Clockwork Gnomes", "Clockwork Steed", "Clockwork Swarm", "Coral Reef", "Dark Maze", "Daughter of Autumn", "Death Speakers", "Didgeridoo", "Drudge Spell", "Dry Spell", "Dwarven Pony", "Dwarven Sea Clan", "Dwarven Trader", "Ebony Rhino", "Eron the Relentless", "Evaporate", "Faerie Noble", "Feast of the Unicorn", "Feroz's Ban", "Folk of An-Havva", "Forget", "Funeral March", "Ghost Hounds", "Giant Albatross", "Giant Oyster", "Grandmother Sengir", "Greater Werewolf", "Hazduhr the Abbot", "Headstone", "Heart Wolf", "Hungry Mist", "Ihsan's Shade", "Irini Sengir", "Ironclaw Curse", "Jinx", "Joven", "Joven's Ferrets", "Joven's Tools", "Koskun Falls", "Koskun Keep", "Labyrinth Minotaur", "Leaping Lizard", "Leeches", "Mammoth Harness", "Marjhan", "Memory Lapse", "Merchant Scroll", "Mesa Falcon", "Mystic Decree", "Narwhal", "Orcish Mine", "Primal Order", "Prophecy", "Rashka the Slayer", "Reef Pirates", "Renewal", "Retribution", "Reveka, Wizard Savant", "Root Spider", "Roots", "Roterothopter", "Rysorian Badger", "Samite Alchemist", "Sea Sprite", "Sea Troll", "Sengir Autocrat", "Sengir Bats", "Serra Aviary", "Serra Bestiary", "Serra Inquisitors", "Serra Paladin", "Serrated Arrows", "Shrink", "Soraya the Falconer", "Spectral Bears", "Timmerian Fiends", "Torture", "Trade Caravan", "Truce", "Veldrane of Sengir", "Wall of Kelp", "Willow Faerie", "Willow Priestess", "Winter Sky", "Wizards' School"); + List namePredicates = new ArrayList<>(); + for (String name: nameStrings) { + namePredicates.add(new NamePredicate(name)); + } + filter.add(TokenPredicate.FALSE); + filter.add(Predicates.or(namePredicates)); } public ApocalypseChime(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/a/ArborealAlliance.java b/Mage.Sets/src/mage/cards/a/ArborealAlliance.java new file mode 100644 index 00000000000..1697b52e705 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArborealAlliance.java @@ -0,0 +1,72 @@ +package mage.cards.a; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.PopulateEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.token.SylvanOfferingTreefolkToken; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class ArborealAlliance extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.ELF, "Elves"); + + public ArborealAlliance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{X}{G}{G}"); + + // When Arboreal Alliance enters the battlefield, create an X/X green Treefolk creature token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ArborealAllianceEffect())); + + // Whenever you attack with one or more Elves, populate. + this.addAbility(new AttacksWithCreaturesTriggeredAbility( + new PopulateEffect(), 1, filter + )); + } + + private ArborealAlliance(final ArborealAlliance card) { + super(card); + } + + @Override + public ArborealAlliance copy() { + return new ArborealAlliance(this); + } +} + +class ArborealAllianceEffect extends OneShotEffect { + + ArborealAllianceEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "create an X/X green Treefolk creature token"; + } + + private ArborealAllianceEffect(final ArborealAllianceEffect effect) { + super(effect); + } + + @Override + public ArborealAllianceEffect copy() { + return new ArborealAllianceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return new CreateTokenEffect( + new SylvanOfferingTreefolkToken(ManacostVariableValue.ETB.calculate(game, source, this)) + ).apply(game, source); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/ArcadesTheStrategist.java b/Mage.Sets/src/mage/cards/a/ArcadesTheStrategist.java index 1f5e3ee9625..0e8511a5306 100644 --- a/Mage.Sets/src/mage/cards/a/ArcadesTheStrategist.java +++ b/Mage.Sets/src/mage/cards/a/ArcadesTheStrategist.java @@ -6,7 +6,7 @@ import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderAllEffect; -import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessAllEffect; +import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessControlledEffect; import mage.abilities.keyword.DefenderAbility; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VigilanceAbility; @@ -25,12 +25,10 @@ import java.util.UUID; public final class ArcadesTheStrategist extends CardImpl { private static final FilterControlledCreaturePermanent defenderSingle = new FilterControlledCreaturePermanent("a creature with defender"); - private static final FilterCreaturePermanent defenderPlural = new FilterCreaturePermanent("Each creature you control with defender"); + private static final FilterCreaturePermanent defenderPlural = new FilterCreaturePermanent("creature you control with defender"); static { defenderSingle.add(new AbilityPredicate(DefenderAbility.class)); - - defenderPlural.add(TargetController.YOU.getControllerPredicate()); defenderPlural.add(new AbilityPredicate(DefenderAbility.class)); } @@ -55,7 +53,7 @@ public final class ArcadesTheStrategist extends CardImpl { )); // Each creature you control with defender assigns combat damage equal to its toughness rather than its power and can attack as though it didn't have defender. - Ability ability = new SimpleStaticAbility(new CombatDamageByToughnessAllEffect(defenderPlural)); + Ability ability = new SimpleStaticAbility(new CombatDamageByToughnessControlledEffect(defenderPlural)); ability.addEffect(new CanAttackAsThoughItDidntHaveDefenderAllEffect( Duration.WhileOnBattlefield, defenderSingle ).setText("and can attack as though it didn't have defender")); diff --git a/Mage.Sets/src/mage/cards/a/AshenmoorLiege.java b/Mage.Sets/src/mage/cards/a/AshenmoorLiege.java index 9671893a004..ca02268669b 100644 --- a/Mage.Sets/src/mage/cards/a/AshenmoorLiege.java +++ b/Mage.Sets/src/mage/cards/a/AshenmoorLiege.java @@ -1,25 +1,19 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.LoseLifeTargetEffect; 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.Zone; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** * @@ -35,7 +29,6 @@ public final class AshenmoorLiege extends CardImpl { filterRedCreature.add(new ColorPredicate(ObjectColor.RED)); } - public AshenmoorLiege(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B/R}{B/R}{B/R}"); this.subtype.add(SubType.ELEMENTAL); @@ -50,10 +43,8 @@ public final class AshenmoorLiege extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filterRedCreature, true))); // Whenever Ashenmoor Liege becomes the target of a spell or ability an opponent controls, that player loses 4 life. - this.addAbility(new AshenmoorLiegeTriggeredAbility()); - - - + this.addAbility(new BecomesTargetSourceTriggeredAbility(new LoseLifeTargetEffect(4), + StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.PLAYER, false)); } private AshenmoorLiege(final AshenmoorLiege card) { @@ -65,39 +56,3 @@ public final class AshenmoorLiege extends CardImpl { return new AshenmoorLiege(this); } } - -class AshenmoorLiegeTriggeredAbility extends TriggeredAbilityImpl { - - public AshenmoorLiegeTriggeredAbility() { - super(Zone.BATTLEFIELD, new LoseLifeTargetEffect(4), false); - } - - private AshenmoorLiegeTriggeredAbility(final AshenmoorLiegeTriggeredAbility ability) { - super(ability); - } - - @Override - public AshenmoorLiegeTriggeredAbility copy() { - return new AshenmoorLiegeTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability an opponent controls, that player loses 4 life."; - } - -} diff --git a/Mage.Sets/src/mage/cards/a/AshesOfTheAbhorrent.java b/Mage.Sets/src/mage/cards/a/AshesOfTheAbhorrent.java index 5b37e2b0bd0..01ca1445e75 100644 --- a/Mage.Sets/src/mage/cards/a/AshesOfTheAbhorrent.java +++ b/Mage.Sets/src/mage/cards/a/AshesOfTheAbhorrent.java @@ -59,11 +59,6 @@ class AshesOfTheAbhorrentEffect extends ContinuousRuleModifyingEffectImpl { return new AshesOfTheAbhorrentEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL diff --git a/Mage.Sets/src/mage/cards/a/AshiokDreamRender.java b/Mage.Sets/src/mage/cards/a/AshiokDreamRender.java index da888faf9e9..efdd92108df 100644 --- a/Mage.Sets/src/mage/cards/a/AshiokDreamRender.java +++ b/Mage.Sets/src/mage/cards/a/AshiokDreamRender.java @@ -66,11 +66,6 @@ class AshiokDreamRenderEffect extends ContinuousRuleModifyingEffectImpl { return new AshiokDreamRenderEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/a/AshioksErasure.java b/Mage.Sets/src/mage/cards/a/AshioksErasure.java index e12a1acceb3..a8a4273cb13 100644 --- a/Mage.Sets/src/mage/cards/a/AshioksErasure.java +++ b/Mage.Sets/src/mage/cards/a/AshioksErasure.java @@ -151,11 +151,6 @@ class AshioksErasureReplacementEffect extends ContinuousRuleModifyingEffectImpl return false; } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public AshioksErasureReplacementEffect copy() { return new AshioksErasureReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/a/AssaultFormation.java b/Mage.Sets/src/mage/cards/a/AssaultFormation.java index 1ece2730289..e2ba98baffc 100644 --- a/Mage.Sets/src/mage/cards/a/AssaultFormation.java +++ b/Mage.Sets/src/mage/cards/a/AssaultFormation.java @@ -1,4 +1,3 @@ - package mage.cards.a; import mage.abilities.Ability; @@ -7,14 +6,13 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderTargetEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; -import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessAllEffect; +import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessControlledEffect; import mage.abilities.keyword.DefenderAbility; 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 mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.target.common.TargetCreaturePermanent; @@ -36,7 +34,7 @@ public final class AssaultFormation extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); // Each creature you control assigns combat damage equal to its toughness rather than its power. - this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_EACH))); + this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessControlledEffect())); // {G}: Target creature with defender can attack this turn as though it didn't have defender. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CanAttackAsThoughItDidntHaveDefenderTargetEffect(Duration.EndOfTurn), new ManaCostsImpl<>("{G}")); diff --git a/Mage.Sets/src/mage/cards/a/AssaultSuit.java b/Mage.Sets/src/mage/cards/a/AssaultSuit.java index 32dc67888c5..d180d8c7191 100644 --- a/Mage.Sets/src/mage/cards/a/AssaultSuit.java +++ b/Mage.Sets/src/mage/cards/a/AssaultSuit.java @@ -1,7 +1,6 @@ package mage.cards.a; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -25,14 +24,15 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class AssaultSuit extends CardImpl { public AssaultSuit(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +2/+2, has haste, can't attack you or a planeswalker you control, and can't be sacrificed. @@ -65,7 +65,7 @@ public final class AssaultSuit extends CardImpl { } } - class AssaultSuitCantBeSacrificed extends ContinuousRuleModifyingEffectImpl { +class AssaultSuitCantBeSacrificed extends ContinuousRuleModifyingEffectImpl { public AssaultSuitCantBeSacrificed() { super(Duration.WhileOnBattlefield, Outcome.Detriment, true, false); @@ -81,25 +81,20 @@ public final class AssaultSuit extends CardImpl { return new AssaultSuitCantBeSacrificed(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { return "This creature can't be sacrificed."; } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SACRIFICE_PERMANENT; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.SACRIFICE_PERMANENT) { - Permanent equipment = game.getPermanent(source.getSourceId()); - if (equipment != null) { - return equipment.isAttachedTo(event.getTargetId()); - } - } - return false; + Permanent equipment = game.getPermanent(source.getSourceId()); + return equipment != null && equipment.isAttachedTo(event.getTargetId()); } } diff --git a/Mage.Sets/src/mage/cards/a/AstralDragon.java b/Mage.Sets/src/mage/cards/a/AstralDragon.java index 4e4d72b2b7f..59640b2b6a5 100644 --- a/Mage.Sets/src/mage/cards/a/AstralDragon.java +++ b/Mage.Sets/src/mage/cards/a/AstralDragon.java @@ -42,7 +42,7 @@ public final class AstralDragon extends CardImpl { false, null, 3, 3, true); effect.setText("create two tokens that are copies of target noncreature permanent, " + "except they're 3/3 Dragon creatures in addition to their other types, and they have flying"); - effect.setAdditionalSubType(SubType.DRAGON); + effect.withAdditionalSubType(SubType.DRAGON); Ability ability = new EntersBattlefieldTriggeredAbility(effect); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability.withFlavorWord("Project Image")); diff --git a/Mage.Sets/src/mage/cards/a/AureliasFury.java b/Mage.Sets/src/mage/cards/a/AureliasFury.java index 3ce2d3b5357..142b6ad8307 100644 --- a/Mage.Sets/src/mage/cards/a/AureliasFury.java +++ b/Mage.Sets/src/mage/cards/a/AureliasFury.java @@ -133,11 +133,6 @@ class AureliasFuryCantCastEffect extends ContinuousRuleModifyingEffectImpl { return new AureliasFuryCantCastEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -176,22 +171,27 @@ class AureliasFuryDamagedByWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - MageObject obj = game.getObject(event.getSourceId()); - if (!(obj instanceof Spell) || !sourceId.equals(((Spell) obj).getSourceId())) { - return; - } switch (event.getType()) { case DAMAGED_PERMANENT: Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature(game)) { + if (isOurSource(event, game) && permanent != null && permanent.isCreature(game)) { damagedCreatures.add(event.getTargetId()); } - return; + break; case DAMAGED_PLAYER: - damagedPlayers.add(event.getTargetId()); + if (isOurSource(event, game)) { + damagedPlayers.add(event.getTargetId()); + } + break; } } + private boolean isOurSource(GameEvent event, Game game) { + // must call after event filter + MageObject obj = game.getObject(event.getSourceId()); + return obj instanceof Spell && sourceId.equals(((Spell) obj).getSourceId()); + } + @Override public void reset() { super.reset(); diff --git a/Mage.Sets/src/mage/cards/a/AzorTheLawbringer.java b/Mage.Sets/src/mage/cards/a/AzorTheLawbringer.java index bc24da77fc8..243fd1d5148 100644 --- a/Mage.Sets/src/mage/cards/a/AzorTheLawbringer.java +++ b/Mage.Sets/src/mage/cards/a/AzorTheLawbringer.java @@ -106,11 +106,6 @@ class AzorTheLawbringerCantCastEffect extends ContinuousRuleModifyingEffectImpl return new AzorTheLawbringerCantCastEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -120,6 +115,11 @@ class AzorTheLawbringerCantCastEffect extends ContinuousRuleModifyingEffectImpl return null; } + @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) { UUID opponentId = getTargetPointer().getFirst(game, source); diff --git a/Mage.Sets/src/mage/cards/b/BaldinCenturyHerdmaster.java b/Mage.Sets/src/mage/cards/b/BaldinCenturyHerdmaster.java index 57061918a18..0a545ef0130 100644 --- a/Mage.Sets/src/mage/cards/b/BaldinCenturyHerdmaster.java +++ b/Mage.Sets/src/mage/cards/b/BaldinCenturyHerdmaster.java @@ -16,7 +16,6 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -37,7 +36,7 @@ public final class BaldinCenturyHerdmaster extends CardImpl { // As long as it's your turn, each creature assigns combat damage equal to its toughness rather than its power. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( - new CombatDamageByToughnessAllEffect(StaticFilters.FILTER_PERMANENT_CREATURE), + new CombatDamageByToughnessAllEffect(), MyTurnCondition.instance, "as long as it's your turn, each creature " + "assigns combat damage equal to its toughness rather than its power" ))); diff --git a/Mage.Sets/src/mage/cards/b/BallroomBrawlers.java b/Mage.Sets/src/mage/cards/b/BallroomBrawlers.java index f8122dfd019..47e53fe5d60 100644 --- a/Mage.Sets/src/mage/cards/b/BallroomBrawlers.java +++ b/Mage.Sets/src/mage/cards/b/BallroomBrawlers.java @@ -39,7 +39,7 @@ public final class BallroomBrawlers extends CardImpl { this.toughness = new MageInt(5); // Whenever Ballroom Brawlers attacks, Ballroom Brawlers and up to one other target creature you control each gain your choice of first strike or lifelink until end of turn. - Ability ability = new AttacksTriggeredAbility(new BallroomBrawlersEffect()).setReplaceRuleText(false); + Ability ability = new AttacksTriggeredAbility(new BallroomBrawlersEffect()).withRuleTextReplacement(false); ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/Banefire.java b/Mage.Sets/src/mage/cards/b/Banefire.java index c4455c32cbe..a28fa7ddd20 100644 --- a/Mage.Sets/src/mage/cards/b/Banefire.java +++ b/Mage.Sets/src/mage/cards/b/Banefire.java @@ -124,16 +124,12 @@ class BanefireCantCounterEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - return true; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COUNTER; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() != GameEvent.EventType.COUNTER) { - return false; - } - Card card = game.getCard(source.getSourceId()); if (card == null) { return false; diff --git a/Mage.Sets/src/mage/cards/b/BartelRuneaxe.java b/Mage.Sets/src/mage/cards/b/BartelRuneaxe.java index f99b9415112..05af9222790 100644 --- a/Mage.Sets/src/mage/cards/b/BartelRuneaxe.java +++ b/Mage.Sets/src/mage/cards/b/BartelRuneaxe.java @@ -65,11 +65,6 @@ class BartelRuneaxeEffect extends ContinuousRuleModifyingEffectImpl { return event.getType() == GameEvent.EventType.TARGET; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/b/BasandraBattleSeraph.java b/Mage.Sets/src/mage/cards/b/BasandraBattleSeraph.java index 26e1ff0f94d..44eb59b16af 100644 --- a/Mage.Sets/src/mage/cards/b/BasandraBattleSeraph.java +++ b/Mage.Sets/src/mage/cards/b/BasandraBattleSeraph.java @@ -72,11 +72,6 @@ class BasandraBattleSeraphEffect extends ContinuousRuleModifyingEffectImpl { return new BasandraBattleSeraphEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage.Sets/src/mage/cards/b/BasilicaScreecher.java b/Mage.Sets/src/mage/cards/b/BasilicaScreecher.java index 848ed8ebe12..a536e58d977 100644 --- a/Mage.Sets/src/mage/cards/b/BasilicaScreecher.java +++ b/Mage.Sets/src/mage/cards/b/BasilicaScreecher.java @@ -26,7 +26,7 @@ public final class BasilicaScreecher extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // Extort (Whenever you cast a spell, you pay {WB}. If you do, each opponent loses 1 life and you gain that much life.) + // Extort (Whenever you cast a spell, you may pay {WB}. If you do, each opponent loses 1 life and you gain that much life.) this.addAbility(new ExtortAbility()); } diff --git a/Mage.Sets/src/mage/cards/b/BattleMammoth.java b/Mage.Sets/src/mage/cards/b/BattleMammoth.java index 27095dc028a..d5f86b409b6 100644 --- a/Mage.Sets/src/mage/cards/b/BattleMammoth.java +++ b/Mage.Sets/src/mage/cards/b/BattleMammoth.java @@ -1,14 +1,16 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.common.BecomesTargetOpponentAllTriggeredAbility; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.ForetellAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; +import mage.filter.StaticFilters; import java.util.UUID; @@ -28,8 +30,8 @@ public final class BattleMammoth extends CardImpl { 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 BecomesTargetOpponentAllTriggeredAbility( - new DrawCardSourceControllerEffect(1), true + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true )); // Foretell {2}{G}{G} diff --git a/Mage.Sets/src/mage/cards/b/BelligerentBrontodon.java b/Mage.Sets/src/mage/cards/b/BelligerentBrontodon.java index f5342dff7f4..f1c22a9de4d 100644 --- a/Mage.Sets/src/mage/cards/b/BelligerentBrontodon.java +++ b/Mage.Sets/src/mage/cards/b/BelligerentBrontodon.java @@ -1,15 +1,13 @@ - package mage.cards.b; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessAllEffect; +import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessControlledEffect; 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; /** @@ -25,7 +23,7 @@ public final class BelligerentBrontodon extends CardImpl { this.toughness = new MageInt(6); // Each creature you control assigns combat damage equal to its toughness rather than its power. - this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_EACH))); + this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessControlledEffect())); } private BelligerentBrontodon(final BelligerentBrontodon card) { diff --git a/Mage.Sets/src/mage/cards/b/BenthicExplorers.java b/Mage.Sets/src/mage/cards/b/BenthicExplorers.java index 98333b18c7d..b4998813171 100644 --- a/Mage.Sets/src/mage/cards/b/BenthicExplorers.java +++ b/Mage.Sets/src/mage/cards/b/BenthicExplorers.java @@ -19,11 +19,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetLandPermanent; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import java.util.*; /** * @author jeffwadsworth @@ -110,22 +106,32 @@ class BenthicExplorersManaEffect extends ManaEffect { @Override public Mana produceMana(Game game, Ability source) { Mana mana = new Mana(); - if (game == null) { return mana; } - - Choice choice = ManaType.getChoiceOfManaTypes(getManaTypes(game, source), false); - if (choice.getChoices().isEmpty()) { return mana; } + if (game == null) { + return mana; + } Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return mana; + } + + Choice choice = ManaType.getChoiceOfManaTypes(getManaTypes(game, source), false); + if (choice.getChoices().isEmpty()) { + return mana; + } + + if (choice.getChoices().size() == 1) { choice.setChoice(choice.getChoices().iterator().next()); } else { - if (player == null - || !player.choose(Outcome.Neutral, choice, game)) { + if (!player.choose(Outcome.PutManaInPool, choice, game)) { return mana; } } - if (choice.getChoice() == null) { return mana; } + if (choice.getChoice() == null) { + return mana; + } switch (choice.getChoice()) { case "Black": @@ -160,7 +166,9 @@ class BenthicExplorersManaEffect extends ManaEffect { List untapped = (List) game.getState() .getValue("UntapTargetCost" + source.getSourceId().toString()); Permanent land = game.getPermanentOrLKIBattlefield(untapped.get(0)); - if (land == null) { return types; } + if (land == null) { + return types; + } Abilities mana = land.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD); for (ActivatedManaAbilityImpl ability : mana) { diff --git a/Mage.Sets/src/mage/cards/b/Berserk.java b/Mage.Sets/src/mage/cards/b/Berserk.java index 69f3b196a34..ec13f4322ae 100644 --- a/Mage.Sets/src/mage/cards/b/Berserk.java +++ b/Mage.Sets/src/mage/cards/b/Berserk.java @@ -72,20 +72,20 @@ class BerserkReplacementEffect extends ContinuousRuleModifyingEffectImpl { super(effect); } + @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 (event.getType() == GameEvent.EventType.CAST_SPELL && event.getSourceId().equals(source.getSourceId())) { + if (event.getSourceId().equals(source.getSourceId())) { CombatDamageStepStartedWatcher watcher = game.getState().getWatcher(CombatDamageStepStartedWatcher.class); return watcher == null || watcher.conditionMet(); } return false; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public BerserkReplacementEffect copy() { return new BerserkReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/b/BindTheMonster.java b/Mage.Sets/src/mage/cards/b/BindTheMonster.java index 579d49a2439..d4c19b21dac 100644 --- a/Mage.Sets/src/mage/cards/b/BindTheMonster.java +++ b/Mage.Sets/src/mage/cards/b/BindTheMonster.java @@ -73,7 +73,7 @@ class BindTheMonsterEffect extends OneShotEffect { @Override public boolean apply (Game game, Ability source) { - Permanent attachment = source.getSourcePermanentIfItStillExists(game); + Permanent attachment = source.getSourcePermanentOrLKI(game); if (attachment != null) { Permanent creature = game.getPermanent(attachment.getAttachedTo()); if (creature != null) { diff --git a/Mage.Sets/src/mage/cards/b/BlindingBeam.java b/Mage.Sets/src/mage/cards/b/BlindingBeam.java index f54373c94aa..e71d3871c8f 100644 --- a/Mage.Sets/src/mage/cards/b/BlindingBeam.java +++ b/Mage.Sets/src/mage/cards/b/BlindingBeam.java @@ -108,11 +108,6 @@ class BlindingBeamEffect2 extends ContinuousRuleModifyingEffectImpl { return new BlindingBeamEffect2(this); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public boolean isInactive(Ability source, Game game) { // the PRE step part is directly after the UNTAP events for permanents diff --git a/Mage.Sets/src/mage/cards/b/BloodFrenzy.java b/Mage.Sets/src/mage/cards/b/BloodFrenzy.java index 79841bdb040..b924d12984a 100644 --- a/Mage.Sets/src/mage/cards/b/BloodFrenzy.java +++ b/Mage.Sets/src/mage/cards/b/BloodFrenzy.java @@ -79,9 +79,4 @@ class BloodFrenzyCastRestriction extends ContinuousRuleModifyingEffectImpl { } return false; } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } } diff --git a/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java b/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java index 33c743ba440..8bbd3c1e2b2 100644 --- a/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java +++ b/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java @@ -1,7 +1,7 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.DamageCantBePreventedEffect; import mage.cards.AdventureCard; @@ -28,11 +28,11 @@ public final class BonecrusherGiant extends AdventureCard { this.toughness = new MageInt(3); // Whenever Bonecrusher Giant becomes the target of a spell, Bonecrusher Giant deals 2 damage to that spell's controller. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new DamageTargetEffect( 2, true, "that spell's controller", "{this}" - ), StaticFilters.FILTER_SPELL_A, SetTargetPointer.PLAYER - ).setTriggerPhrase("Whenever {this} becomes the target of a spell, ")); + ), StaticFilters.FILTER_SPELL_A, SetTargetPointer.PLAYER, false) + .withRuleTextReplacement(false)); // Stomp // Damage can’t be prevented this turn. Stomp deals 2 damage to any target. diff --git a/Mage.Sets/src/mage/cards/b/BoneshardSlasher.java b/Mage.Sets/src/mage/cards/b/BoneshardSlasher.java index f3d65493a77..201c7cd21ee 100644 --- a/Mage.Sets/src/mage/cards/b/BoneshardSlasher.java +++ b/Mage.Sets/src/mage/cards/b/BoneshardSlasher.java @@ -4,7 +4,7 @@ package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CardsInControllerGraveyardCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -35,7 +35,7 @@ public final class BoneshardSlasher extends CardImpl { Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), new CardsInControllerGraveyardCondition(7), "As long as seven or more cards are in your graveyard, {this} gets +2/+2")); - Effect effect = new ConditionalContinuousEffect(new GainAbilitySourceEffect(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())), + Effect effect = new ConditionalContinuousEffect(new GainAbilitySourceEffect(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())), new CardsInControllerGraveyardCondition(7), "and has \"When {this} becomes the target of a spell or ability, sacrifice it.\""); ability.addEffect(effect); ability.setAbilityWord(AbilityWord.THRESHOLD); diff --git a/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java b/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java index 1bdb9609a0f..5a8e25285b0 100644 --- a/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java +++ b/Mage.Sets/src/mage/cards/b/BoseijuWhoSheltersAll.java @@ -105,11 +105,6 @@ class BoseijuWhoSheltersAllCantCounterEffect extends ContinuousRuleModifyingEffe return new BoseijuWhoSheltersAllCantCounterEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject sourceObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/b/BoundDetermined.java b/Mage.Sets/src/mage/cards/b/BoundDetermined.java index 36e0bc360ab..4dd698b2f82 100644 --- a/Mage.Sets/src/mage/cards/b/BoundDetermined.java +++ b/Mage.Sets/src/mage/cards/b/BoundDetermined.java @@ -119,11 +119,6 @@ class DeterminedEffect extends ContinuousRuleModifyingEffectImpl { return new DeterminedEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject sourceObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/b/BrandOfIllOmen.java b/Mage.Sets/src/mage/cards/b/BrandOfIllOmen.java index e208f80a055..2c00bfbcf22 100644 --- a/Mage.Sets/src/mage/cards/b/BrandOfIllOmen.java +++ b/Mage.Sets/src/mage/cards/b/BrandOfIllOmen.java @@ -66,11 +66,6 @@ class BrandOfIllOmenEffect extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public BrandOfIllOmenEffect copy() { return new BrandOfIllOmenEffect(this); diff --git a/Mage.Sets/src/mage/cards/b/BrenardGingerSculptor.java b/Mage.Sets/src/mage/cards/b/BrenardGingerSculptor.java index e84690f3bc5..5e8a142e5ac 100644 --- a/Mage.Sets/src/mage/cards/b/BrenardGingerSculptor.java +++ b/Mage.Sets/src/mage/cards/b/BrenardGingerSculptor.java @@ -106,8 +106,8 @@ class BrenardGingerSculptorEffect extends OneShotEffect { ); effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game) + 1)); effect.setBecomesArtifact(true); - effect.setAdditionalSubType(SubType.FOOD); - effect.setAdditionalSubType(SubType.GOLEM); + effect.withAdditionalSubType(SubType.FOOD); + effect.withAdditionalSubType(SubType.GOLEM); effect.addAdditionalAbilities(new FoodAbility(false)); player.moveCards(card, Zone.EXILED, source, game); diff --git a/Mage.Sets/src/mage/cards/b/BrineComber.java b/Mage.Sets/src/mage/cards/b/BrineComber.java index b2d5b594d90..959136a2bd4 100644 --- a/Mage.Sets/src/mage/cards/b/BrineComber.java +++ b/Mage.Sets/src/mage/cards/b/BrineComber.java @@ -1,19 +1,19 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.SpellAbility; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.DisturbAbility; +import mage.abilities.meta.OrTriggeredAbility; 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.filter.FilterSpell; +import mage.filter.predicate.other.AuraSpellPredicate; import mage.game.permanent.token.SpiritWhiteToken; -import mage.game.stack.Spell; import java.util.UUID; @@ -22,6 +22,12 @@ import java.util.UUID; */ public final class BrineComber extends CardImpl { + private static final FilterSpell filter = new FilterSpell("an Aura spell"); + + static { + filter.add(AuraSpellPredicate.instance); + } + public BrineComber(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); @@ -31,7 +37,10 @@ public final class BrineComber extends CardImpl { this.secondSideCardClazz = mage.cards.b.BrineboundGift.class; // Whenever Brine Comber enters the battlefield or becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying. - this.addAbility(new BrineComberTriggeredAbility()); + this.addAbility(new OrTriggeredAbility(Zone.ALL, new CreateTokenEffect(new SpiritWhiteToken()), false, + "Whenever {this} enters the battlefield or becomes the target of an Aura spell, ", + new EntersBattlefieldTriggeredAbility(null), + new BecomesTargetSourceTriggeredAbility(null, filter))); // Disturb {W}{U} this.addAbility(new DisturbAbility(this, "{W}{U}")); @@ -46,54 +55,3 @@ public final class BrineComber extends CardImpl { return new BrineComber(this); } } - -class BrineComberTriggeredAbility extends TriggeredAbilityImpl { - - BrineComberTriggeredAbility() { - super(Zone.ALL, new CreateTokenEffect(new SpiritWhiteToken())); - } - - private BrineComberTriggeredAbility(final BrineComberTriggeredAbility effect) { - super(effect); - } - - @Override - public BrineComberTriggeredAbility copy() { - return new BrineComberTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD - || event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - switch (event.getType()) { - case ENTERS_THE_BATTLEFIELD: - return event.getTargetId().equals(getSourceId()); - case TARGETED: - break; - default: - return false; - } - if (this.getSourcePermanentIfItStillExists(game) == null - || !event.getTargetId().equals(getSourceId())) { - return false; - } - Spell spell = game.getSpell(event.getSourceId()); - if(spell == null) { - return false; - } - SpellAbility spellAbility = (SpellAbility) spell.getStackAbility(); - return spellAbility != null - && spellAbility.getCharacteristics(game).hasSubtype(SubType.AURA, game); - } - - @Override - public String getRule() { - return "Whenever {this} enters the battlefield or becomes the target " + - "of an Aura spell, create a 1/1 white Spirit creature token with flying."; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BrineboundGift.java b/Mage.Sets/src/mage/cards/b/BrineboundGift.java index 530f797aa62..7789d0a53b3 100644 --- a/Mage.Sets/src/mage/cards/b/BrineboundGift.java +++ b/Mage.Sets/src/mage/cards/b/BrineboundGift.java @@ -1,23 +1,20 @@ package mage.cards.b; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAttachedTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.keyword.EnchantAbility; +import mage.abilities.meta.OrTriggeredAbility; 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.permanent.Permanent; +import mage.constants.*; +import mage.filter.FilterSpell; +import mage.filter.predicate.other.AuraSpellPredicate; import mage.game.permanent.token.SpiritWhiteToken; -import mage.game.stack.Spell; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -28,6 +25,12 @@ import java.util.UUID; */ public final class BrineboundGift extends CardImpl { + private static final FilterSpell filter = new FilterSpell("an Aura spell"); + + static { + filter.add(AuraSpellPredicate.instance); + } + public BrineboundGift(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); @@ -44,7 +47,10 @@ public final class BrineboundGift extends CardImpl { this.addAbility(ability); // Whenever Brinebound Gift enters the battlefield or enchanted creature becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying. - this.addAbility(new BrineboundGiftTriggeredAbility()); + this.addAbility(new OrTriggeredAbility(Zone.ALL, new CreateTokenEffect(new SpiritWhiteToken()), false, + "Whenever {this} enters the battlefield or enchanted creature becomes the target of an Aura spell, ", + new EntersBattlefieldTriggeredAbility(null), + new BecomesTargetAttachedTriggeredAbility(null, filter, SetTargetPointer.NONE, false))); // If Brinebound Gift would be put into a graveyard from anywhere, exile it instead. this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead"))); @@ -59,49 +65,3 @@ public final class BrineboundGift extends CardImpl { return new BrineboundGift(this); } } - -class BrineboundGiftTriggeredAbility extends TriggeredAbilityImpl { - - BrineboundGiftTriggeredAbility() { - super(Zone.ALL, new CreateTokenEffect(new SpiritWhiteToken())); - } - - private BrineboundGiftTriggeredAbility(final BrineboundGiftTriggeredAbility effect) { - super(effect); - } - - @Override - public BrineboundGiftTriggeredAbility copy() { - return new BrineboundGiftTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD - || event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - switch (event.getType()) { - case ENTERS_THE_BATTLEFIELD: - return event.getTargetId().equals(getSourceId()); - case TARGETED: - break; - default: - return false; - } - Permanent permanent = this.getSourcePermanentOrLKI(game); - if (permanent == null || !event.getTargetId().equals(permanent.getAttachedTo())) { - return false; - } - Spell spell = game.getSpell(event.getSourceId()); - return spell != null && spell.hasSubtype(SubType.AURA, game); - } - - @Override - public String getRule() { - return "Whenever {this} enters the battlefield or enchanted creature becomes the target " + - "of an Aura spell, create a 1/1 white Spirit creature token with flying."; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BriselaVoiceOfNightmares.java b/Mage.Sets/src/mage/cards/b/BriselaVoiceOfNightmares.java index 8a3a672a9ce..8a9ba49f3c8 100644 --- a/Mage.Sets/src/mage/cards/b/BriselaVoiceOfNightmares.java +++ b/Mage.Sets/src/mage/cards/b/BriselaVoiceOfNightmares.java @@ -71,11 +71,6 @@ class BriselaVoiceOfNightmaresCantCastEffect extends ContinuousRuleModifyingEffe return new BriselaVoiceOfNightmaresCantCastEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/c/CallForthTheTempest.java b/Mage.Sets/src/mage/cards/c/CallForthTheTempest.java new file mode 100644 index 00000000000..20b1a339f07 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CallForthTheTempest.java @@ -0,0 +1,85 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageAllEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.CascadeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.watchers.common.SpellsCastWatcher; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class CallForthTheTempest extends CardImpl { + + private static Hint hint = new ValueHint("Total mana value of other spells you've cast this turn", CallForthTheTempestDynamicValue.instance); + + public CallForthTheTempest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{R}{R}{R}"); + + // Cascade + this.addAbility(new CascadeAbility(false).setRuleAtTheTop(true)); + + // Cascade + this.addAbility(new CascadeAbility(false).setRuleAtTheTop(true)); + + // Call Forth the Tempest deals damage to each creature your opponents control equal to the total mana value of other spells you've cast this turn. + this.getSpellAbility().addEffect(new DamageAllEffect( + CallForthTheTempestDynamicValue.instance, + StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE + )); + this.getSpellAbility().addHint(hint); + } + + private CallForthTheTempest(final CallForthTheTempest card) { + super(card); + } + + @Override + public CallForthTheTempest copy() { + return new CallForthTheTempest(this); + } +} + +enum CallForthTheTempestDynamicValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); + if (watcher == null) { + return 0; + } + + return watcher + .getSpellsCastThisTurn(sourceAbility.getControllerId()) + .stream() + .filter(s -> s != null && !s.getSourceId().equals(sourceAbility.getSourceId())) + .mapToInt(s -> s.getManaValue()) + .sum(); + } + + @Override + public CallForthTheTempestDynamicValue copy() { + return this; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "total mana value of other spells you've cast this turn"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/CaptainPhasma.java b/Mage.Sets/src/mage/cards/c/CaptainPhasma.java index 4a454ff8398..9039211278b 100644 --- a/Mage.Sets/src/mage/cards/c/CaptainPhasma.java +++ b/Mage.Sets/src/mage/cards/c/CaptainPhasma.java @@ -25,8 +25,8 @@ import mage.target.common.TargetCardInLibrary; */ public final class CaptainPhasma extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Nontoken trooper creatures"); - private static final FilterCreatureCard filterCard = new FilterCreatureCard("Trooper card"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken Trooper creatures"); + private static final FilterCreatureCard filterCard = new FilterCreatureCard("Trooper creature card"); static { filter.add(SubType.TROOPER.getPredicate()); @@ -44,11 +44,9 @@ public final class CaptainPhasma extends CardImpl { this.toughness = new MageInt(4); // Nontoken Trooper creatures you control have "When this creature enters the battlefield, create 1/1/ white Trooper creature token." - Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new TrooperToken()) - .setText("When this creature enters the battlefield, create a 1/1 white Trooper creature token"), - false, true); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityControlledEffect(ability, Duration.WhileOnBattlefield, filter, false))); + Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new TrooperToken()), false) + .setTriggerPhrase("When this creature enters the battlefield, "); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(ability, Duration.WhileOnBattlefield, filter, false))); // {W}{U}{B}{R}{G}: Search your library for a Trooper creature card, reveal it, put it into your hand, then shuffle your library. this.addAbility(new SimpleActivatedAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filterCard), true), new ManaCostsImpl<>("{W}{U}{B}{R}{G}"))); diff --git a/Mage.Sets/src/mage/cards/c/CavernOfSouls.java b/Mage.Sets/src/mage/cards/c/CavernOfSouls.java index f5ab69ab977..3b0663ce566 100644 --- a/Mage.Sets/src/mage/cards/c/CavernOfSouls.java +++ b/Mage.Sets/src/mage/cards/c/CavernOfSouls.java @@ -177,11 +177,6 @@ class CavernOfSoulsCantCounterEffect extends ContinuousRuleModifyingEffectImpl { return new CavernOfSoulsCantCounterEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject sourceObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/c/CephalidAristocrat.java b/Mage.Sets/src/mage/cards/c/CephalidAristocrat.java index 15cc200f35e..e3ada73de32 100644 --- a/Mage.Sets/src/mage/cards/c/CephalidAristocrat.java +++ b/Mage.Sets/src/mage/cards/c/CephalidAristocrat.java @@ -1,7 +1,7 @@ package mage.cards.c; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.MillCardsControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -22,7 +22,7 @@ public final class CephalidAristocrat extends CardImpl { this.toughness = new MageInt(3); // Whenever Cephalid Aristocrat becomes the target of a spell or ability, put the top two cards of your library into your graveyard. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new MillCardsControllerEffect(2))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new MillCardsControllerEffect(2))); } private CephalidAristocrat(final CephalidAristocrat card) { diff --git a/Mage.Sets/src/mage/cards/c/CephalidIllusionist.java b/Mage.Sets/src/mage/cards/c/CephalidIllusionist.java index e8f205d8d5c..966d3f3fa40 100644 --- a/Mage.Sets/src/mage/cards/c/CephalidIllusionist.java +++ b/Mage.Sets/src/mage/cards/c/CephalidIllusionist.java @@ -4,7 +4,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; @@ -35,7 +35,7 @@ public final class CephalidIllusionist extends CardImpl { this.toughness = new MageInt(1); // Whenever Cephalid Illusionist becomes the target of a spell or ability, put the top three cards of your library into your graveyard. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new MillCardsControllerEffect(3))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new MillCardsControllerEffect(3))); // {2}{U}, {tap}: Prevent all combat damage that would be dealt to Effect effect = new PreventDamageToTargetEffect(Duration.EndOfTurn, true); diff --git a/Mage.Sets/src/mage/cards/c/ChromeMox.java b/Mage.Sets/src/mage/cards/c/ChromeMox.java index 6d524368a9b..55fba9d82ca 100644 --- a/Mage.Sets/src/mage/cards/c/ChromeMox.java +++ b/Mage.Sets/src/mage/cards/c/ChromeMox.java @@ -192,7 +192,7 @@ class ChromeMoxManaEffect extends ManaEffect { if (choice.getChoices().size() == 1) { choice.setChoice(choice.getChoices().iterator().next()); } else { - if (!player.choose(outcome, choice, game)) { + if (!player.choose(Outcome.PutManaInPool, choice, game)) { return mana; } } diff --git a/Mage.Sets/src/mage/cards/c/CircleOfDreamsDruid.java b/Mage.Sets/src/mage/cards/c/CircleOfDreamsDruid.java index eeacb87ff1f..68e97644e44 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfDreamsDruid.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfDreamsDruid.java @@ -5,6 +5,7 @@ import mage.MageInt; import mage.Mana; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.hint.ValueHint; import mage.abilities.mana.DynamicManaAbility; import mage.constants.SubType; import mage.cards.CardImpl; @@ -29,7 +30,7 @@ public final class CircleOfDreamsDruid extends CardImpl { this.toughness = new MageInt(1); // {T}: Add {G} for each creature you control. - this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), xValue)); + this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), xValue).addHint(new ValueHint("Creatures you control", xValue))); } private CircleOfDreamsDruid(final CircleOfDreamsDruid card) { diff --git a/Mage.Sets/src/mage/cards/c/CircuDimirLobotomist.java b/Mage.Sets/src/mage/cards/c/CircuDimirLobotomist.java index 7f625814ec4..f14dd3a10c3 100644 --- a/Mage.Sets/src/mage/cards/c/CircuDimirLobotomist.java +++ b/Mage.Sets/src/mage/cards/c/CircuDimirLobotomist.java @@ -120,10 +120,14 @@ class CircuDimirLobotomistRuleModifyingEffect extends ContinuousRuleModifyingEff return "You can't cast this spell because a card with the same name is exiled by " + CardUtil.getSourceName(game, source) + '.'; } + @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 (event.getType() != GameEvent.EventType.CAST_SPELL - || !game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { + if (!game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { return false; } MageObject object = game.getObject(event.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/c/CityInABottle.java b/Mage.Sets/src/mage/cards/c/CityInABottle.java index db902f71f0a..b4486030a90 100644 --- a/Mage.Sets/src/mage/cards/c/CityInABottle.java +++ b/Mage.Sets/src/mage/cards/c/CityInABottle.java @@ -15,6 +15,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; @@ -22,16 +23,24 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.UUID; -import static mage.cards.c.CityInABottle.getArabianNightsNamePredicates; - /** * @author emerald000 */ public final class CityInABottle extends CardImpl { + // Arabian Nights names per CR 206.3a + static final List ARABIAN_NIGHTS_CARD_NAME_PREDICATES = new ArrayList<>(); + static { + List nameStrings = Arrays.asList("Abu Ja'far", "Aladdin", "Aladdin's Lamp", "Aladdin's Ring", "Ali Baba", "Ali from Cairo", "Army of Allah", "Bazaar of Baghdad", "Bird Maiden", "Bottle of Suleiman", "Brass Man", "Camel", "City in a Bottle", "City of Brass", "Cuombajj Witches", "Cyclone", "Dancing Scimitar", "Dandan", "Desert", "Desert Nomads", "Desert Twister", "Diamond Valley", "Drop of Honey", "Ebony Horse", "Elephant Graveyard", "El-Hajjaj", "Erg Raiders", "Erhnam Djinn", "Eye for an Eye", "Fishliver Oil", "Flying Carpet", "Flying Men", "Ghazban Ogre", "Giant Tortoise", "Guardian Beast", "Hasran Ogress", "Hurr Jackal", "Ifh-Biff Efreet", "Island Fish Jasconius", "Island of Wak-Wak", "Jandor's Ring", "Jandor's Saddlebags", "Jeweled Bird", "Jihad", "Junun Efreet", "Juzam Djinn", "Khabal Ghoul", "King Suleiman", "Kird Ape", "Library of Alexandria", "Magnetic Mountain", "Merchant Ship", "Metamorphosis", "Mijae Djinn", "Moorish Cavalry", "Nafs Asp", "Oasis", "Old Man of the Sea", "Oubliette", "Piety", "Pyramids", "Repentant Blacksmith", "Ring of Ma'ruf", "Rukh Egg", "Sandals of Abdallah", "Sandstorm", "Serendib Djinn", "Serendib Efreet", "Shahrazad", "Sindbad", "Singing Tree", "Sorceress Queen", "Stone-Throwing Devils", "Unstable Mutation", "War Elephant", "Wyluli Wolf", "Ydwen Efreet"); + for (String name : nameStrings) { + ARABIAN_NIGHTS_CARD_NAME_PREDICATES.add(new NamePredicate(name)); + } + } + public CityInABottle(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); @@ -39,7 +48,7 @@ public final class CityInABottle extends CardImpl { this.addAbility(new CityInABottleStateTriggeredAbility()); // Players can't play cards originally printed in the Arabian Nights expansion. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CityInABottleCantPlayEffect())); + this.addAbility(new SimpleStaticAbility(new CityInABottleCantPlayEffect())); } private CityInABottle(final CityInABottle card) { @@ -51,100 +60,20 @@ public final class CityInABottle extends CardImpl { return new CityInABottle(this); } - static public List getArabianNightsNamePredicates() { - List namePredicatesArabianNights = new ArrayList<>(); - namePredicatesArabianNights.add(new NamePredicate("Abu Ja'far")); - namePredicatesArabianNights.add(new NamePredicate("Aladdin")); - namePredicatesArabianNights.add(new NamePredicate("Aladdin's Lamp")); - namePredicatesArabianNights.add(new NamePredicate("Aladdin's Ring")); - namePredicatesArabianNights.add(new NamePredicate("Ali Baba")); - namePredicatesArabianNights.add(new NamePredicate("Ali from Cairo")); - namePredicatesArabianNights.add(new NamePredicate("Army of Allah")); - namePredicatesArabianNights.add(new NamePredicate("Bazaar of Baghdad")); - namePredicatesArabianNights.add(new NamePredicate("Bird Maiden")); - namePredicatesArabianNights.add(new NamePredicate("Bottle of Suleiman")); - namePredicatesArabianNights.add(new NamePredicate("Brass Man")); - namePredicatesArabianNights.add(new NamePredicate("Camel")); - namePredicatesArabianNights.add(new NamePredicate("City of Brass")); - namePredicatesArabianNights.add(new NamePredicate("Cuombajj Witches")); - namePredicatesArabianNights.add(new NamePredicate("Cyclone")); - namePredicatesArabianNights.add(new NamePredicate("Dancing Scimitar")); - namePredicatesArabianNights.add(new NamePredicate("Dandan")); - namePredicatesArabianNights.add(new NamePredicate("Desert")); - namePredicatesArabianNights.add(new NamePredicate("Desert Nomads")); - namePredicatesArabianNights.add(new NamePredicate("Desert Twister")); - namePredicatesArabianNights.add(new NamePredicate("Diamond Valley")); - namePredicatesArabianNights.add(new NamePredicate("Drop of Honey")); - namePredicatesArabianNights.add(new NamePredicate("Ebony Horse")); - namePredicatesArabianNights.add(new NamePredicate("El-Hajjaj")); - namePredicatesArabianNights.add(new NamePredicate("Elephant Graveyard")); - namePredicatesArabianNights.add(new NamePredicate("Erg Raiders")); - namePredicatesArabianNights.add(new NamePredicate("Erhnam Djinn")); - namePredicatesArabianNights.add(new NamePredicate("Eye for an Eye")); - namePredicatesArabianNights.add(new NamePredicate("Fishliver Oil")); - namePredicatesArabianNights.add(new NamePredicate("Flying Carpet")); - namePredicatesArabianNights.add(new NamePredicate("Flying Men")); - namePredicatesArabianNights.add(new NamePredicate("Ghazban Ogre")); - namePredicatesArabianNights.add(new NamePredicate("Giant Tortoise")); - namePredicatesArabianNights.add(new NamePredicate("Guardian Beast")); - namePredicatesArabianNights.add(new NamePredicate("Hasran Ogress")); - namePredicatesArabianNights.add(new NamePredicate("Hurr Jackal")); - namePredicatesArabianNights.add(new NamePredicate("Ifh-Biff Efreet")); - namePredicatesArabianNights.add(new NamePredicate("Island Fish Jasconius")); - namePredicatesArabianNights.add(new NamePredicate("Island of Wak-Wak")); - namePredicatesArabianNights.add(new NamePredicate("Jandor's Ring")); - namePredicatesArabianNights.add(new NamePredicate("Jandor's Saddlebags")); - namePredicatesArabianNights.add(new NamePredicate("Jeweled Bird")); - namePredicatesArabianNights.add(new NamePredicate("Jihad")); - namePredicatesArabianNights.add(new NamePredicate("Junun Efreet")); - namePredicatesArabianNights.add(new NamePredicate("Juzam Djinn")); - namePredicatesArabianNights.add(new NamePredicate("Khabal Ghoul")); - namePredicatesArabianNights.add(new NamePredicate("King Suleiman")); - namePredicatesArabianNights.add(new NamePredicate("Kird Ape")); - namePredicatesArabianNights.add(new NamePredicate("Library of Alexandria")); - namePredicatesArabianNights.add(new NamePredicate("Magnetic Mountain")); - namePredicatesArabianNights.add(new NamePredicate("Merchant Ship")); - namePredicatesArabianNights.add(new NamePredicate("Metamorphosis")); - namePredicatesArabianNights.add(new NamePredicate("Mijae Djinn")); - namePredicatesArabianNights.add(new NamePredicate("Moorish Cavalry")); - namePredicatesArabianNights.add(new NamePredicate("Nafs Asp")); - namePredicatesArabianNights.add(new NamePredicate("Oasis")); - namePredicatesArabianNights.add(new NamePredicate("Old Man of the Sea")); - namePredicatesArabianNights.add(new NamePredicate("Oubliette")); - namePredicatesArabianNights.add(new NamePredicate("Piety")); - namePredicatesArabianNights.add(new NamePredicate("Pyramids")); - namePredicatesArabianNights.add(new NamePredicate("Repentant Blacksmith")); - namePredicatesArabianNights.add(new NamePredicate("Ring of Ma'ruf")); - namePredicatesArabianNights.add(new NamePredicate("Rukh Egg")); - namePredicatesArabianNights.add(new NamePredicate("Sandals of Abdallah")); - namePredicatesArabianNights.add(new NamePredicate("Sandstorm")); - namePredicatesArabianNights.add(new NamePredicate("Serendib Djinn")); - namePredicatesArabianNights.add(new NamePredicate("Serendib Efreet")); - namePredicatesArabianNights.add(new NamePredicate("Shahrazad")); - namePredicatesArabianNights.add(new NamePredicate("Sindbad")); - namePredicatesArabianNights.add(new NamePredicate("Singing Tree")); - namePredicatesArabianNights.add(new NamePredicate("Sorceress Queen")); - namePredicatesArabianNights.add(new NamePredicate("Stone-Throwing Devils")); - namePredicatesArabianNights.add(new NamePredicate("Unstable Mutation")); - namePredicatesArabianNights.add(new NamePredicate("War Elephant")); - namePredicatesArabianNights.add(new NamePredicate("Wyluli Wolf")); - namePredicatesArabianNights.add(new NamePredicate("Ydwen Efreet")); - return namePredicatesArabianNights; - } } class CityInABottleStateTriggeredAbility extends StateTriggeredAbility { - private static final FilterPermanent filter = new FilterPermanent("a nontoken permanent originally printed in the Arabian Nights expansion other than City in a Bottle"); - + private static final FilterPermanent filter = new FilterPermanent("other nontoken permanents with a name originally printed in the Arabian Nights expansion"); static { filter.add(TokenPredicate.FALSE); - filter.add(Predicates.or(getArabianNightsNamePredicates())); - + filter.add(Predicates.or(CityInABottle.ARABIAN_NIGHTS_CARD_NAME_PREDICATES)); + filter.add(AnotherPredicate.instance); } CityInABottleStateTriggeredAbility() { super(Zone.BATTLEFIELD, new CityInABottleSacrificeEffect()); + setTriggerPhrase("Whenever one or more other nontoken permanents with a name originally printed in the Arabian Nights expansion are on the battlefield, "); } private CityInABottleStateTriggeredAbility(final CityInABottleStateTriggeredAbility ability) { @@ -161,24 +90,21 @@ class CityInABottleStateTriggeredAbility extends StateTriggeredAbility { return game.getBattlefield().contains(filter, this.getControllerId(), this, game, 1); } - @Override - public String getRule() { - return "Whenever one or more other nontoken permanents with a name originally printed in the Arabian Nights expansion are on the battlefield, their controllers sacrifice them"; - } } class CityInABottleSacrificeEffect extends OneShotEffect { - private static final FilterPermanent filter = new FilterPermanent("a nontoken permanent originally printed in the Arabian Nights expansion other than City in a Bottle"); + private static final FilterPermanent filter = new FilterPermanent("other nontoken permanents with a name originally printed in the Arabian Nights expansion"); static { filter.add(TokenPredicate.FALSE); - filter.add(Predicates.or(getArabianNightsNamePredicates())); + filter.add(Predicates.or(CityInABottle.ARABIAN_NIGHTS_CARD_NAME_PREDICATES)); + filter.add(AnotherPredicate.instance); } CityInABottleSacrificeEffect() { super(Outcome.Sacrifice); - this.staticText = "its controller sacrifices it"; + this.staticText = "their controllers sacrifice them"; } private CityInABottleSacrificeEffect(final CityInABottleSacrificeEffect effect) { @@ -201,26 +127,21 @@ class CityInABottleSacrificeEffect extends OneShotEffect { class CityInABottleCantPlayEffect extends ContinuousRuleModifyingEffectImpl { - private static final FilterCard filter = new FilterCard("cards originally printed in the Arabian Nights expansion"); + private static final FilterCard filter = new FilterCard(); static { - filter.add(Predicates.or(getArabianNightsNamePredicates())); + filter.add(Predicates.or(CityInABottle.ARABIAN_NIGHTS_CARD_NAME_PREDICATES)); } CityInABottleCantPlayEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "Players can't play cards originally printed in the Arabian Nights expansion"; + staticText = "Players can't cast spells or play lands with a name originally printed in the Arabian Nights expansion"; } private CityInABottleCantPlayEffect(final CityInABottleCantPlayEffect effect) { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public CityInABottleCantPlayEffect copy() { return new CityInABottleCantPlayEffect(this); @@ -228,7 +149,7 @@ class CityInABottleCantPlayEffect extends ContinuousRuleModifyingEffectImpl { @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { - return "You can't play cards originally printed in the Arabian Nights expansion"; + return "You can't play cards with a name originally printed in the Arabian Nights expansion"; } @Override diff --git a/Mage.Sets/src/mage/cards/c/CloudCover.java b/Mage.Sets/src/mage/cards/c/CloudCover.java index 79d6553ad70..eb0670d0de6 100644 --- a/Mage.Sets/src/mage/cards/c/CloudCover.java +++ b/Mage.Sets/src/mage/cards/c/CloudCover.java @@ -1,31 +1,35 @@ - package mage.cards.c; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; 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.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; +import mage.constants.SetTargetPointer; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; /** * - * @author fireshoes + * @author xenohedron */ public final class CloudCover extends CardImpl { + private static final FilterPermanent filter = new FilterControlledPermanent("another permanent you control"); + static { + filter.add(AnotherPredicate.instance); + } + public CloudCover(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{U}"); // Whenever another permanent you control becomes the target of a spell or ability an opponent controls, you may return that permanent to its owner's hand. - this.addAbility(new CloudCoverAbility()); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new ReturnToHandTargetEffect().setText("return that permanent to its owner's hand"), + filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.PERMANENT, true)); } private CloudCover(final CloudCover card) { @@ -37,44 +41,3 @@ public final class CloudCover extends CardImpl { return new CloudCover(this); } } - -class CloudCoverAbility extends TriggeredAbilityImpl { - - public CloudCoverAbility() { - super(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), true); - } - - private CloudCoverAbility(final CloudCoverAbility ability) { - super(ability); - } - - @Override - public CloudCoverAbility copy() { - return new CloudCoverAbility(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.getPermanent(event.getTargetId()); - Player controller = game.getPlayer(this.getControllerId()); - if (permanent != null - && permanent.isControlledBy(getControllerId()) - && !permanent.getId().equals(this.getSourceId()) - && controller != null - && controller.hasOpponent(event.getPlayerId(), game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever another permanent you control becomes the target of a spell or ability an opponent controls, you may return that permanent to its owner's hand."; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CloudsteelKirin.java b/Mage.Sets/src/mage/cards/c/CloudsteelKirin.java index 63b8ed6f133..dde6497a1c9 100644 --- a/Mage.Sets/src/mage/cards/c/CloudsteelKirin.java +++ b/Mage.Sets/src/mage/cards/c/CloudsteelKirin.java @@ -71,8 +71,9 @@ class CloudsteelKirinEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - return true; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.WINS + || event.getType() == GameEvent.EventType.LOSES; } @Override diff --git a/Mage.Sets/src/mage/cards/c/ConjurersBan.java b/Mage.Sets/src/mage/cards/c/ConjurersBan.java index 474af05d512..6e09f380966 100644 --- a/Mage.Sets/src/mage/cards/c/ConjurersBan.java +++ b/Mage.Sets/src/mage/cards/c/ConjurersBan.java @@ -53,24 +53,22 @@ class ConjurersBanEffect extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public ConjurersBanEffect copy() { return new ConjurersBanEffect(this); } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CAST_SPELL + || event.getType() == GameEvent.EventType.PLAY_LAND; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.CAST_SPELL || event.getType() == GameEvent.EventType.PLAY_LAND) { - MageObject object = game.getObject(event.getSourceId()); - String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - return CardUtil.haveSameNames(object, cardName, game); - } - return false; + MageObject object = game.getObject(event.getSourceId()); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return CardUtil.haveSameNames(object, cardName, game); } @Override diff --git a/Mage.Sets/src/mage/cards/c/ConquerorsFlail.java b/Mage.Sets/src/mage/cards/c/ConquerorsFlail.java index bae13527317..151c57f97e8 100644 --- a/Mage.Sets/src/mage/cards/c/ConquerorsFlail.java +++ b/Mage.Sets/src/mage/cards/c/ConquerorsFlail.java @@ -101,11 +101,6 @@ class ConquerorsFlailEffect extends ContinuousRuleModifyingEffectImpl { return new ConquerorsFlailEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage.Sets/src/mage/cards/c/CorneredMarket.java b/Mage.Sets/src/mage/cards/c/CorneredMarket.java index 0306a8d7b17..72c7679c0ce 100644 --- a/Mage.Sets/src/mage/cards/c/CorneredMarket.java +++ b/Mage.Sets/src/mage/cards/c/CorneredMarket.java @@ -110,11 +110,6 @@ class CorneredMarketReplacementEffect extends ContinuousRuleModifyingEffectImpl return false; } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public CorneredMarketReplacementEffect copy() { return new CorneredMarketReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/c/CorruptedGrafstone.java b/Mage.Sets/src/mage/cards/c/CorruptedGrafstone.java index 251ced30140..4155be08527 100644 --- a/Mage.Sets/src/mage/cards/c/CorruptedGrafstone.java +++ b/Mage.Sets/src/mage/cards/c/CorruptedGrafstone.java @@ -14,6 +14,7 @@ import mage.choices.Choice; import mage.choices.ChoiceColor; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; +import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; @@ -148,7 +149,7 @@ class CorruptedGrafstoneManaEffect extends ManaEffect { if (choice.getChoices().size() == 1) { choice.setChoice(choice.getChoices().iterator().next()); } else { - if (!player.choose(outcome, choice, game)) { + if (!player.choose(Outcome.PutManaInPool, choice, game)) { return mana; } } diff --git a/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java b/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java index f5090da749a..069f9cbb85f 100644 --- a/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java +++ b/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java @@ -64,11 +64,6 @@ class CouncilOfTheAbsoluteReplacementEffect extends ContinuousRuleModifyingEffec super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public CouncilOfTheAbsoluteReplacementEffect copy() { return new CouncilOfTheAbsoluteReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/c/Cowardice.java b/Mage.Sets/src/mage/cards/c/Cowardice.java index b3c8219bc9e..2440ca90769 100644 --- a/Mage.Sets/src/mage/cards/c/Cowardice.java +++ b/Mage.Sets/src/mage/cards/c/Cowardice.java @@ -1,21 +1,16 @@ - package mage.cards.c; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; 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.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author dustinconrad + * @author xenohedron */ public final class Cowardice extends CardImpl { @@ -23,7 +18,7 @@ public final class Cowardice extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}{U}"); // Whenever a creature becomes the target of a spell or ability, return that creature to its owner's hand. - this.addAbility(new CowardiceTriggeredAbility()); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new ReturnToHandTargetEffect(), StaticFilters.FILTER_PERMANENT_A_CREATURE)); } private Cowardice(final Cowardice card) { @@ -35,39 +30,3 @@ public final class Cowardice extends CardImpl { return new Cowardice(this); } } - -class CowardiceTriggeredAbility extends TriggeredAbilityImpl { - - public CowardiceTriggeredAbility() { - super(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), false); - } - - private CowardiceTriggeredAbility(final CowardiceTriggeredAbility ability) { - super(ability); - } - - @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.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature(game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId(), game)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature becomes the target of a spell or ability, return that creature to its owner's hand."; - } - - @Override - public CowardiceTriggeredAbility copy() { - return new CowardiceTriggeredAbility(this); - } -} diff --git a/Mage.Sets/src/mage/cards/c/CreatureBond.java b/Mage.Sets/src/mage/cards/c/CreatureBond.java index bbbffff4248..74c51c35783 100644 --- a/Mage.Sets/src/mage/cards/c/CreatureBond.java +++ b/Mage.Sets/src/mage/cards/c/CreatureBond.java @@ -13,7 +13,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -57,12 +56,7 @@ enum CreatureBondValue implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(sourceAbility.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(sourceAbility.getSourceId()); - } + Permanent enchantment = sourceAbility.getSourcePermanentOrLKI(game); if (enchantment == null) { return 0; } diff --git a/Mage.Sets/src/mage/cards/c/CrystallineNautilus.java b/Mage.Sets/src/mage/cards/c/CrystallineNautilus.java index 5e5f14cbf69..bedc6207572 100644 --- a/Mage.Sets/src/mage/cards/c/CrystallineNautilus.java +++ b/Mage.Sets/src/mage/cards/c/CrystallineNautilus.java @@ -4,7 +4,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.SacrificeSourceEffect; @@ -36,13 +36,13 @@ public final class CrystallineNautilus extends CardImpl { this.addAbility(new BestowAbility(this, "{3}{U}{U}")); // When Crystalline Nautilus becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); // Enchanted creature gets +4/+4 and has "When this creature becomes the target of a spell or ability, sacrifice it." Effect effect = new BoostEnchantedEffect(4,4,Duration.WhileOnBattlefield); effect.setText("Enchanted creature gets +4/+4"); Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); - effect = new GainAbilityAttachedEffect(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect()), AttachmentType.AURA, Duration.WhileOnBattlefield); + effect = new GainAbilityAttachedEffect(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect()), AttachmentType.AURA, Duration.WhileOnBattlefield); effect.setText("and has \"When this creature becomes the target of a spell or ability, sacrifice it.\""); ability.addEffect(effect); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/CurseOfBounty.java b/Mage.Sets/src/mage/cards/c/CurseOfBounty.java index 0809bc9368b..006f6836826 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfBounty.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfBounty.java @@ -18,7 +18,6 @@ import mage.target.targetpointer.FixedTarget; import java.util.UUID; import mage.abilities.common.EnchantedPlayerAttackedTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.constants.Zone; import mage.filter.StaticFilters; import mage.players.Player; @@ -71,12 +70,7 @@ class CurseOfBountyEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment != null) { Player enchantedPlayer = game.getPlayer(enchantment.getAttachedTo()); if (enchantedPlayer != null) { diff --git a/Mage.Sets/src/mage/cards/c/CurseOfDisturbance.java b/Mage.Sets/src/mage/cards/c/CurseOfDisturbance.java index c00f23f0a68..72e2a0eecf2 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfDisturbance.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfDisturbance.java @@ -15,7 +15,6 @@ 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.ZombieToken; @@ -72,12 +71,7 @@ class CurseOfDisturbanceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment != null) { Player enchantedPlayer = game.getPlayer(enchantment.getAttachedTo()); if (enchantedPlayer != null) { diff --git a/Mage.Sets/src/mage/cards/c/CurseOfExhaustion.java b/Mage.Sets/src/mage/cards/c/CurseOfExhaustion.java index 6049a0d29b1..ff5dc6dd78d 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfExhaustion.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfExhaustion.java @@ -19,13 +19,12 @@ import mage.watchers.common.CastSpellLastTurnWatcher; import java.util.UUID; /** - * * @author BetaSteward */ public final class CurseOfExhaustion extends CardImpl { public CurseOfExhaustion(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); this.subtype.add(SubType.AURA, SubType.CURSE); @@ -66,22 +65,18 @@ class CurseOfExhaustionEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - return true; + 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 (event.getType() == GameEvent.EventType.CAST_SPELL) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Player player = game.getPlayer(enchantment.getAttachedTo()); - if (player != null && event.getPlayerId().equals(player.getId())) { - CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); - if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) > 0) { - return true; - } - } + Permanent enchantment = game.getPermanent(source.getSourceId()); + if (enchantment != null && enchantment.getAttachedTo() != null) { + Player player = game.getPlayer(enchantment.getAttachedTo()); + if (player != null && event.getPlayerId().equals(player.getId())) { + CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); + return watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) > 0; } } return false; diff --git a/Mage.Sets/src/mage/cards/c/CurseOfOpulence.java b/Mage.Sets/src/mage/cards/c/CurseOfOpulence.java index 2694e0f6778..786ce7db5f3 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfOpulence.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfOpulence.java @@ -22,7 +22,6 @@ import mage.target.targetpointer.FixedTarget; import java.util.HashSet; import java.util.Set; import java.util.UUID; -import mage.constants.Zone; /** * @author Saga @@ -72,12 +71,7 @@ class CurseOfOpulenceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/c/CurseOfVerbosity.java b/Mage.Sets/src/mage/cards/c/CurseOfVerbosity.java index f0b42b14d11..cca22b63173 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfVerbosity.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfVerbosity.java @@ -15,7 +15,6 @@ 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; @@ -70,12 +69,7 @@ class CurseOfVerbosityEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment != null) { Player enchantedPlayer = game.getPlayer(enchantment.getAttachedTo()); if (enchantedPlayer != null) { diff --git a/Mage.Sets/src/mage/cards/c/CurseOfVitality.java b/Mage.Sets/src/mage/cards/c/CurseOfVitality.java index 095acb8d9c5..f172e205f86 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfVitality.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfVitality.java @@ -19,7 +19,6 @@ import mage.abilities.Ability; import mage.abilities.common.EnchantedPlayerAttackedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GainLifeTargetEffect; -import mage.constants.Zone; import mage.players.Player; /** @@ -70,12 +69,7 @@ class CurseOfVitalityEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment != null) { Player enchantedPlayer = game.getPlayer(enchantment.getAttachedTo()); if (enchantedPlayer != null) { diff --git a/Mage.Sets/src/mage/cards/c/CursedMonstrosity.java b/Mage.Sets/src/mage/cards/c/CursedMonstrosity.java index 15eccc15810..fff7e9ec068 100644 --- a/Mage.Sets/src/mage/cards/c/CursedMonstrosity.java +++ b/Mage.Sets/src/mage/cards/c/CursedMonstrosity.java @@ -3,7 +3,7 @@ package mage.cards.c; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; import mage.abilities.keyword.FlyingAbility; @@ -30,10 +30,10 @@ public final class CursedMonstrosity extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever Cursed Monstrosity becomes the target of a spell or ability, sacrifice it unless you discard a land card. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new SacrificeSourceUnlessPaysEffect( new DiscardTargetCost(new TargetCardInHand(new FilterLandCard())) - ))); + )).withRuleTextReplacement(true)); } private CursedMonstrosity(final CursedMonstrosity card) { diff --git a/Mage.Sets/src/mage/cards/d/DampingEngine.java b/Mage.Sets/src/mage/cards/d/DampingEngine.java index 2c734cd9719..390008e1067 100644 --- a/Mage.Sets/src/mage/cards/d/DampingEngine.java +++ b/Mage.Sets/src/mage/cards/d/DampingEngine.java @@ -105,11 +105,6 @@ class DampingEngineEffect extends ContinuousRuleModifyingEffectImpl { return new DampingEngineEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = source.getSourceObject(game); diff --git a/Mage.Sets/src/mage/cards/d/DanithaBenaliasHope.java b/Mage.Sets/src/mage/cards/d/DanithaBenaliasHope.java index 2957f5a5c86..9193a5c64f1 100644 --- a/Mage.Sets/src/mage/cards/d/DanithaBenaliasHope.java +++ b/Mage.Sets/src/mage/cards/d/DanithaBenaliasHope.java @@ -1,17 +1,14 @@ package mage.cards.d; -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.constants.*; import mage.abilities.keyword.FirstStrikeAbility; -import mage.abilities.keyword.VigilanceAbility; import mage.abilities.keyword.LifelinkAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.*; +import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.card.AuraCardCanAttachToPermanentId; @@ -19,8 +16,8 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; -import mage.target.common.TargetCardInHand; -import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; /** * @@ -84,29 +81,27 @@ class DanithaBenaliasHopeEffect extends OneShotEffect { } Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); UUID sourcePermanentId = sourcePermanent == null ? null : sourcePermanent.getId(); + String sourcePermanentName = sourcePermanent == null ? "" : sourcePermanent.getName(); FilterCard filter = new FilterCard("an Aura or Equipment card"); filter.add(Predicates.or( Predicates.and(SubType.AURA.getPredicate(), new AuraCardCanAttachToPermanentId(sourcePermanentId)), SubType.EQUIPMENT.getPredicate() )); - TargetCard target; - if (controller.chooseUse(outcome, "Look in Hand or Graveyard?", null, "Hand", "Graveyard", source, game)) { - target = new TargetCardInHand(filter); - } else { - target = new TargetCardInYourGraveyard(filter); - } + Cards cards = new CardsImpl(); + cards.addAllCards(controller.getHand().getCards(filter, game)); + cards.addAllCards(controller.getGraveyard().getCards(filter, game)); + TargetCard target = new TargetCard(Zone.ALL, filter); target.withNotTarget(true); - if (target.canChoose(controller.getId(), source, game)) { - controller.chooseTarget(outcome, target, source, game); - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - if (sourcePermanent != null) { - game.getState().setValue("attachTo:" + card.getId(), sourcePermanent); - } - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - if (sourcePermanent != null) { - sourcePermanent.addAttachment(card.getId(), source, game); - } + target.withChooseHint("to attach to " + sourcePermanentName); + controller.choose(outcome, cards, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + if (sourcePermanent != null) { + game.getState().setValue("attachTo:" + card.getId(), sourcePermanent); + } + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + if (sourcePermanent != null) { + sourcePermanent.addAttachment(card.getId(), source, game); } } return true; diff --git a/Mage.Sets/src/mage/cards/d/DaruSpiritualist.java b/Mage.Sets/src/mage/cards/d/DaruSpiritualist.java index 244e13e3987..6121b15037f 100644 --- a/Mage.Sets/src/mage/cards/d/DaruSpiritualist.java +++ b/Mage.Sets/src/mage/cards/d/DaruSpiritualist.java @@ -1,27 +1,25 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; 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.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; + +import java.util.UUID; /** - * - * @author L_J + * @author xenohedron */ public final class DaruSpiritualist extends CardImpl { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.CLERIC, "a Cleric creature you control"); + public DaruSpiritualist(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.subtype.add(SubType.HUMAN); @@ -31,7 +29,7 @@ public final class DaruSpiritualist extends CardImpl { this.toughness = new MageInt(1); // Whenever a Cleric creature you control becomes the target of a spell or ability, it gets +0/+2 until end of turn. - this.addAbility(new DaruSpiritualistTriggeredAbility(new BoostTargetEffect(0, 2, Duration.EndOfTurn))); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new BoostTargetEffect(0, 2, Duration.EndOfTurn), filter)); } private DaruSpiritualist(final DaruSpiritualist card) { @@ -43,39 +41,3 @@ public final class DaruSpiritualist extends CardImpl { return new DaruSpiritualist(this); } } - -class DaruSpiritualistTriggeredAbility extends TriggeredAbilityImpl { - - public DaruSpiritualistTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private DaruSpiritualistTriggeredAbility(final DaruSpiritualistTriggeredAbility ability) { - super(ability); - } - - @Override - public DaruSpiritualistTriggeredAbility copy() { - return new DaruSpiritualistTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature != null && creature.hasSubtype(SubType.CLERIC, game) && creature.getControllerId().equals(getControllerId()) && creature.isCreature(game)) { - this.getEffects().setTargetPointer(new FixedTarget(creature, game)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever a Cleric creature you control becomes the target of a spell or ability, it gets +0/+2 until end of turn."; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DeafeningSilence.java b/Mage.Sets/src/mage/cards/d/DeafeningSilence.java index 7f7980d78d9..551df36cac9 100644 --- a/Mage.Sets/src/mage/cards/d/DeafeningSilence.java +++ b/Mage.Sets/src/mage/cards/d/DeafeningSilence.java @@ -61,11 +61,6 @@ class DeafeningSilenceEffect extends ContinuousRuleModifyingEffectImpl { return new DeafeningSilenceEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage.Sets/src/mage/cards/d/DelightedHalfling.java b/Mage.Sets/src/mage/cards/d/DelightedHalfling.java index fed158e1308..b0ff88fcd81 100644 --- a/Mage.Sets/src/mage/cards/d/DelightedHalfling.java +++ b/Mage.Sets/src/mage/cards/d/DelightedHalfling.java @@ -148,11 +148,6 @@ class DelightedHalflingCantCounterEffect extends ContinuousRuleModifyingEffectIm return new DelightedHalflingCantCounterEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject sourceObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/d/DepartedDeckhand.java b/Mage.Sets/src/mage/cards/d/DepartedDeckhand.java index 19b0a9b6fbc..2eccac06366 100644 --- a/Mage.Sets/src/mage/cards/d/DepartedDeckhand.java +++ b/Mage.Sets/src/mage/cards/d/DepartedDeckhand.java @@ -2,7 +2,7 @@ package mage.cards.d; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -43,7 +43,7 @@ public final class DepartedDeckhand extends CardImpl { this.toughness = new MageInt(2); // When Departed Deckhand becomes the target of a spell, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A )); diff --git a/Mage.Sets/src/mage/cards/d/DiffusionSliver.java b/Mage.Sets/src/mage/cards/d/DiffusionSliver.java index 6ea1e05da49..5a50eff6738 100644 --- a/Mage.Sets/src/mage/cards/d/DiffusionSliver.java +++ b/Mage.Sets/src/mage/cards/d/DiffusionSliver.java @@ -1,29 +1,27 @@ package mage.cards.d; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * @author LevelX2 + * @author xenohedron */ public final class DiffusionSliver extends CardImpl { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.SLIVER, "a Sliver creature you control"); + public DiffusionSliver(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.SLIVER); @@ -32,7 +30,9 @@ public final class DiffusionSliver extends CardImpl { this.toughness = new MageInt(1); // Whenever a Sliver creature you control becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {2}. - this.addAbility(new DiffusionSliverTriggeredAbility()); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(2)) + .setText("counter that spell or ability unless its controller pays {2}"), + filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); } private DiffusionSliver(final DiffusionSliver card) { @@ -44,48 +44,3 @@ public final class DiffusionSliver extends CardImpl { return new DiffusionSliver(this); } } - -class DiffusionSliverTriggeredAbility extends TriggeredAbilityImpl { - - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.SLIVER); - - DiffusionSliverTriggeredAbility() { - super(Zone.BATTLEFIELD, null); - } - - private DiffusionSliverTriggeredAbility(final DiffusionSliverTriggeredAbility ability) { - super(ability); - } - - @Override - public DiffusionSliverTriggeredAbility copy() { - return new DiffusionSliverTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - return false; - } - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature == null || !filter.match(creature, getControllerId(), this, game)) { - return false; - } - this.getEffects().clear(); - Effect effect = new CounterUnlessPaysEffect(new GenericManaCost(2)); - effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); - this.addEffect(effect); - return true; - } - - @Override - public String getRule() { - return "Whenever a Sliver creature you control becomes the target of a spell or ability an opponent controls, " + - "counter that spell or ability unless its controller pays {2}."; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DiscipleOfCaelusNin.java b/Mage.Sets/src/mage/cards/d/DiscipleOfCaelusNin.java index 56ce63f957a..25f1103f465 100644 --- a/Mage.Sets/src/mage/cards/d/DiscipleOfCaelusNin.java +++ b/Mage.Sets/src/mage/cards/d/DiscipleOfCaelusNin.java @@ -115,11 +115,6 @@ class DiscipleOfCaelusNinSecondEffect extends ContinuousRuleModifyingEffectImpl return event.getType() == GameEvent.EventType.PHASE_IN; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean applies(GameEvent event, Ability source, Game game) { return true; diff --git a/Mage.Sets/src/mage/cards/d/DismissIntoDream.java b/Mage.Sets/src/mage/cards/d/DismissIntoDream.java index 70340d11ffe..b43d6514179 100644 --- a/Mage.Sets/src/mage/cards/d/DismissIntoDream.java +++ b/Mage.Sets/src/mage/cards/d/DismissIntoDream.java @@ -3,7 +3,7 @@ package mage.cards.d; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.continuous.CreaturesBecomeOtherTypeEffect; @@ -72,7 +72,7 @@ class DismissIntoDreamEffect extends CreaturesBecomeOtherTypeEffect { if (layer == Layer.AbilityAddingRemovingEffects_6) { for (Permanent object: game.getBattlefield().getActivePermanents(this.filter, source.getControllerId(), game)) { - object.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect()), source.getSourceId(), game); + object.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect()), source.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/cards/d/DisplayOfDominance.java b/Mage.Sets/src/mage/cards/d/DisplayOfDominance.java index 5f2f80d1509..8a2f565f58c 100644 --- a/Mage.Sets/src/mage/cards/d/DisplayOfDominance.java +++ b/Mage.Sets/src/mage/cards/d/DisplayOfDominance.java @@ -80,11 +80,6 @@ class DisplayOfDominanceEffect extends ContinuousRuleModifyingEffectImpl { return new DisplayOfDominanceEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.TARGET; diff --git a/Mage.Sets/src/mage/cards/d/DollhouseOfHorrors.java b/Mage.Sets/src/mage/cards/d/DollhouseOfHorrors.java index 9a84a166615..5a679831783 100644 --- a/Mage.Sets/src/mage/cards/d/DollhouseOfHorrors.java +++ b/Mage.Sets/src/mage/cards/d/DollhouseOfHorrors.java @@ -90,7 +90,7 @@ class DollhouseOfHorrorsEffect extends OneShotEffect { false, null, 0, 0, false ); effect.setSavedPermanent(new PermanentCard(CardUtil.getDefaultCardSideForBattlefield(game, card), source.getControllerId(), game)); - effect.setAdditionalSubType(SubType.CONSTRUCT); + effect.withAdditionalSubType(SubType.CONSTRUCT); effect.addAdditionalAbilities(new SimpleStaticAbility(new BoostSourceEffect( xValue, xValue, diff --git a/Mage.Sets/src/mage/cards/d/DoranTheSiegeTower.java b/Mage.Sets/src/mage/cards/d/DoranTheSiegeTower.java index 3997d83a03b..516458c8b23 100644 --- a/Mage.Sets/src/mage/cards/d/DoranTheSiegeTower.java +++ b/Mage.Sets/src/mage/cards/d/DoranTheSiegeTower.java @@ -8,8 +8,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import java.util.UUID; @@ -26,8 +24,6 @@ import java.util.UUID; */ public final class DoranTheSiegeTower extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("each creature"); - public DoranTheSiegeTower(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{B}{G}"); this.supertype.add(SuperType.LEGENDARY); @@ -38,7 +34,7 @@ public final class DoranTheSiegeTower extends CardImpl { this.toughness = new MageInt(5); // Each creature assigns combat damage equal to its toughness rather than its power. - this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessAllEffect(filter))); + this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessAllEffect())); } private DoranTheSiegeTower(final DoranTheSiegeTower card) { diff --git a/Mage.Sets/src/mage/cards/d/DormantGomazoa.java b/Mage.Sets/src/mage/cards/d/DormantGomazoa.java index a1e3732b1c1..f1a3d32dbcb 100644 --- a/Mage.Sets/src/mage/cards/d/DormantGomazoa.java +++ b/Mage.Sets/src/mage/cards/d/DormantGomazoa.java @@ -1,9 +1,8 @@ - package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.BecomesTargetControllerSpellTriggeredAbility; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; @@ -12,8 +11,10 @@ import mage.abilities.keyword.FlyingAbility; 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.StaticFilters; /** * @@ -37,7 +38,8 @@ public final class DormantGomazoa extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepSourceEffect())); // Whenever you become the target of a spell, you may untap Dormant Gomazoa. - this.addAbility(new BecomesTargetControllerSpellTriggeredAbility(new UntapSourceEffect(), true).setTriggerPhrase("Whenever you become the target of a spell, ")); + this.addAbility(new BecomesTargetControllerTriggeredAbility(new UntapSourceEffect(), + null, StaticFilters.FILTER_SPELL_A, SetTargetPointer.NONE, true)); } private DormantGomazoa(final DormantGomazoa card) { diff --git a/Mage.Sets/src/mage/cards/d/DosanTheFallingLeaf.java b/Mage.Sets/src/mage/cards/d/DosanTheFallingLeaf.java index cb41dfe19be..eb4301f0d4d 100644 --- a/Mage.Sets/src/mage/cards/d/DosanTheFallingLeaf.java +++ b/Mage.Sets/src/mage/cards/d/DosanTheFallingLeaf.java @@ -53,13 +53,13 @@ class DosanTheFallingLeafEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return event.getType() == GameEvent.EventType.CAST_SPELL && !game.isActivePlayer(event.getPlayerId()); + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CAST_SPELL; } @Override - public boolean apply(Game game, Ability source) { - return true; + public boolean applies(GameEvent event, Ability source, Game game) { + return !game.isActivePlayer(event.getPlayerId()); } @Override diff --git a/Mage.Sets/src/mage/cards/d/DovinBaan.java b/Mage.Sets/src/mage/cards/d/DovinBaan.java index a9316043013..397bec0fa17 100644 --- a/Mage.Sets/src/mage/cards/d/DovinBaan.java +++ b/Mage.Sets/src/mage/cards/d/DovinBaan.java @@ -80,11 +80,6 @@ class DovinBaanCantActivateAbilitiesEffect extends ContinuousRuleModifyingEffect return new DovinBaanCantActivateAbilitiesEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; diff --git a/Mage.Sets/src/mage/cards/d/DragonlordDromoka.java b/Mage.Sets/src/mage/cards/d/DragonlordDromoka.java index fe9a0f2c8a8..7693af410b1 100644 --- a/Mage.Sets/src/mage/cards/d/DragonlordDromoka.java +++ b/Mage.Sets/src/mage/cards/d/DragonlordDromoka.java @@ -66,11 +66,6 @@ class DragonlordDromokaEffect extends ContinuousRuleModifyingEffectImpl { return new DragonlordDromokaEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage.Sets/src/mage/cards/d/DreadWight.java b/Mage.Sets/src/mage/cards/d/DreadWight.java index 143c354d4af..295f670bc26 100644 --- a/Mage.Sets/src/mage/cards/d/DreadWight.java +++ b/Mage.Sets/src/mage/cards/d/DreadWight.java @@ -182,11 +182,6 @@ class DreadWightDoNotUntapEffect extends ContinuousRuleModifyingEffectImpl { return new DreadWightDoNotUntapEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { Permanent permanentToUntap = game.getPermanent((event.getTargetId())); diff --git a/Mage.Sets/src/mage/cards/d/DreamStrix.java b/Mage.Sets/src/mage/cards/d/DreamStrix.java index 54dd78d5476..556e2a57d6c 100644 --- a/Mage.Sets/src/mage/cards/d/DreamStrix.java +++ b/Mage.Sets/src/mage/cards/d/DreamStrix.java @@ -2,7 +2,7 @@ package mage.cards.d; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.effects.common.LearnEffect; import mage.abilities.effects.common.SacrificeSourceEffect; @@ -32,7 +32,7 @@ public final class DreamStrix extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Dream Strix becomes the target of a spell, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new SacrificeSourceEffect().setText("sacrifice it"), StaticFilters.FILTER_SPELL_A )); diff --git a/Mage.Sets/src/mage/cards/e/EagleOfDeliverance.java b/Mage.Sets/src/mage/cards/e/EagleOfDeliverance.java new file mode 100644 index 00000000000..71c58d6f990 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EagleOfDeliverance.java @@ -0,0 +1,83 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +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.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.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class EagleOfDeliverance extends CardImpl { + + public EagleOfDeliverance(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}"); + + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Eagle of Deliverance enters the battlefield, put an indestructible counter on another target creature you control. Draw a card if that creature's power is 2 or less. + TriggeredAbility trigger = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.INDESTRUCTIBLE.createInstance()) + ); + trigger.addTarget(new TargetControlledCreaturePermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE_YOU_CONTROL)); + trigger.addEffect(new EagleOfDeliveranceEffect()); + this.addAbility(trigger); + } + + private EagleOfDeliverance(final EagleOfDeliverance card) { + super(card); + } + + @Override + public EagleOfDeliverance copy() { + return new EagleOfDeliverance(this); + } +} + +class EagleOfDeliveranceEffect extends OneShotEffect { + + EagleOfDeliveranceEffect() { + super(Outcome.DrawCard); + staticText = "Draw a card if that creature's power is 2 or less"; + } + + private EagleOfDeliveranceEffect(final EagleOfDeliveranceEffect effect) { + super(effect); + } + + @Override + public EagleOfDeliveranceEffect copy() { + return new EagleOfDeliveranceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); + if (permanent == null || permanent.getPower().getValue() > 2) { + return false; + } + return new DrawCardSourceControllerEffect(1).apply(game, source); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/ElrondMasterOfHealing.java b/Mage.Sets/src/mage/cards/e/ElrondMasterOfHealing.java index 8b394ce3735..77509c6b7a9 100644 --- a/Mage.Sets/src/mage/cards/e/ElrondMasterOfHealing.java +++ b/Mage.Sets/src/mage/cards/e/ElrondMasterOfHealing.java @@ -2,18 +2,18 @@ package mage.cards.e; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.BecomesTargetOpponentAllTriggeredAbility; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.common.ScryTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; 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.SuperType; import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.target.common.TargetCreaturePermanent; import mage.target.targetadjustment.TargetAdjuster; @@ -25,13 +25,6 @@ import java.util.UUID; */ public final class ElrondMasterOfHealing extends CardImpl { - private static final FilterPermanent filter - = new FilterControlledCreaturePermanent("a creature you control with a +1/+1 counter on it"); - - static { - filter.add(CounterType.P1P1.getPredicate()); - } - public ElrondMasterOfHealing(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); @@ -48,8 +41,8 @@ public final class ElrondMasterOfHealing extends CardImpl { .setTargetAdjuster(ElrondMasterOfHealingAdjuster.instance)); // Whenever a creature you control with a +1/+1 counter on it becomes the target of a spell or ability an opponent controls, you may draw a card. - this.addAbility(new BecomesTargetOpponentAllTriggeredAbility( - new DrawCardSourceControllerEffect(1), filter, true + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_A_CONTROLLED_CREATURE_P1P1, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true )); } diff --git a/Mage.Sets/src/mage/cards/e/ElvishArchdruid.java b/Mage.Sets/src/mage/cards/e/ElvishArchdruid.java index b1a3396c929..e165a902416 100644 --- a/Mage.Sets/src/mage/cards/e/ElvishArchdruid.java +++ b/Mage.Sets/src/mage/cards/e/ElvishArchdruid.java @@ -8,14 +8,13 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.hint.ValueHint; import mage.abilities.mana.DynamicManaAbility; 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.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; @@ -42,7 +41,7 @@ public final class ElvishArchdruid extends CardImpl { ))); // {T}: Add {G} for each Elf you control. - this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), xValue)); + this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), xValue).addHint(new ValueHint("Elves you control", xValue))); } private ElvishArchdruid(final ElvishArchdruid card) { diff --git a/Mage.Sets/src/mage/cards/e/ElvishEulogist.java b/Mage.Sets/src/mage/cards/e/ElvishEulogist.java index c4b40b65035..b69e04afc71 100644 --- a/Mage.Sets/src/mage/cards/e/ElvishEulogist.java +++ b/Mage.Sets/src/mage/cards/e/ElvishEulogist.java @@ -7,6 +7,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -34,7 +35,8 @@ public final class ElvishEulogist extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainLifeEffect(new CardsInControllerGraveyardCount(filter, 1)) - .setText("you gain 1 life for each Elf card in your graveyard"), new SacrificeSourceCost())); + .setText("you gain 1 life for each Elf card in your graveyard"), new SacrificeSourceCost()) + .addHint(new ValueHint("Elf cards in your graveyard", new CardsInControllerGraveyardCount(filter, 1)))); } private ElvishEulogist(final ElvishEulogist card) { diff --git a/Mage.Sets/src/mage/cards/e/EndlessEvil.java b/Mage.Sets/src/mage/cards/e/EndlessEvil.java index ca810e80490..a90ada8fa81 100644 --- a/Mage.Sets/src/mage/cards/e/EndlessEvil.java +++ b/Mage.Sets/src/mage/cards/e/EndlessEvil.java @@ -42,12 +42,12 @@ public final class EndlessEvil extends CardImpl { this.addAbility(ability); // At the beginning of your upkeep, create a token that’s a copy of enchanted creature, except the token is 1/1. - TriggeredAbility CloneAbility = new BeginningOfUpkeepTriggeredAbility( + TriggeredAbility cloneAbility = new BeginningOfUpkeepTriggeredAbility( new EndlessEvilCloneEffect(), TargetController.YOU, false ); - this.addAbility(CloneAbility); + this.addAbility(cloneAbility); // When enchanted creature dies, if that creature was a Horror, return Endless Evil to its owner’s hand. this.addAbility(new EndlessEvilBounceAbility()); @@ -81,11 +81,7 @@ class EndlessEvilCloneEffect extends OneShotEffect { @Override public boolean apply (Game game, Ability source) { - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // In the case that Endless Evil is blinked - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment != null) { Permanent target = game.getPermanentOrLKIBattlefield(enchantment.getAttachedTo()); if (target != null) { diff --git a/Mage.Sets/src/mage/cards/e/EternalScourge.java b/Mage.Sets/src/mage/cards/e/EternalScourge.java index 390e7a38783..dab0e5cd815 100644 --- a/Mage.Sets/src/mage/cards/e/EternalScourge.java +++ b/Mage.Sets/src/mage/cards/e/EternalScourge.java @@ -1,10 +1,8 @@ - package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.ExileSourceEffect; @@ -12,9 +10,10 @@ 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.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; + +import java.util.UUID; /** * @@ -33,7 +32,8 @@ public final class EternalScourge extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.EXILED, new EternalScourgePlayEffect())); // When Eternal Scourge becomes the target of a spell or ability an opponent controls, exile Eternal Scourge. - this.addAbility(new EternalScourgeAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new ExileSourceEffect(), + StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS)); } private EternalScourge(final EternalScourge card) { @@ -78,37 +78,3 @@ class EternalScourgePlayEffect extends AsThoughEffectImpl { return false; } } - -class EternalScourgeAbility extends TriggeredAbilityImpl { - - public EternalScourgeAbility() { - super(Zone.BATTLEFIELD, new ExileSourceEffect(), false); - } - - private EternalScourgeAbility(final EternalScourgeAbility ability) { - super(ability); - } - - @Override - public EternalScourgeAbility copy() { - return new EternalScourgeAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability an opponent controls, exile {this}."; - } -} diff --git a/Mage.Sets/src/mage/cards/e/ExclusionRitual.java b/Mage.Sets/src/mage/cards/e/ExclusionRitual.java index 15a64c24267..eb1db60b4a2 100644 --- a/Mage.Sets/src/mage/cards/e/ExclusionRitual.java +++ b/Mage.Sets/src/mage/cards/e/ExclusionRitual.java @@ -118,11 +118,6 @@ class ExclusionRitualReplacementEffect extends ContinuousRuleModifyingEffectImpl return false; } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public ExclusionRitualReplacementEffect copy() { return new ExclusionRitualReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/e/ExperimentalFrenzy.java b/Mage.Sets/src/mage/cards/e/ExperimentalFrenzy.java index feb44754499..bfe6f2c1586 100644 --- a/Mage.Sets/src/mage/cards/e/ExperimentalFrenzy.java +++ b/Mage.Sets/src/mage/cards/e/ExperimentalFrenzy.java @@ -66,11 +66,6 @@ class ExperimentalFrenzyRestrictionEffect extends ContinuousRuleModifyingEffectI return new ExperimentalFrenzyRestrictionEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.PLAY_LAND diff --git a/Mage.Sets/src/mage/cards/f/FaithsFetters.java b/Mage.Sets/src/mage/cards/f/FaithsFetters.java index 9c9123c5fc2..d0b672ef39e 100644 --- a/Mage.Sets/src/mage/cards/f/FaithsFetters.java +++ b/Mage.Sets/src/mage/cards/f/FaithsFetters.java @@ -73,11 +73,6 @@ class FaithsFettersEffect extends ContinuousRuleModifyingEffectImpl { return new FaithsFettersEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; diff --git a/Mage.Sets/src/mage/cards/f/FblthpTheLost.java b/Mage.Sets/src/mage/cards/f/FblthpTheLost.java index eb393f990ca..95023f3fe20 100644 --- a/Mage.Sets/src/mage/cards/f/FblthpTheLost.java +++ b/Mage.Sets/src/mage/cards/f/FblthpTheLost.java @@ -1,20 +1,19 @@ package mage.cards.f; import mage.MageInt; -import mage.MageObject; import mage.MageObjectReference; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.stack.Spell; -import mage.target.targetpointer.FixedTarget; import mage.watchers.Watcher; import java.util.HashSet; @@ -38,7 +37,9 @@ public final class FblthpTheLost extends CardImpl { this.addAbility(new FblthpTheLostTriggeredAbility()); // When Fblthp becomes the target of a spell, shuffle Fblthp into its owner's library. - this.addAbility(new FblthpTheLostTargetedTriggeredAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility( + new ShuffleIntoLibrarySourceEffect().setText("shuffle {this} into its owner's library"), + StaticFilters.FILTER_SPELL_A)); } private FblthpTheLost(final FblthpTheLost card) { @@ -128,40 +129,3 @@ class FblthpTheLostWatcher extends Watcher { } } - -class FblthpTheLostTargetedTriggeredAbility extends TriggeredAbilityImpl { - - FblthpTheLostTargetedTriggeredAbility() { - super(Zone.BATTLEFIELD, new ShuffleIntoLibrarySourceEffect(), false); - } - - private FblthpTheLostTargetedTriggeredAbility(final FblthpTheLostTargetedTriggeredAbility ability) { - super(ability); - } - - @Override - public FblthpTheLostTargetedTriggeredAbility copy() { - return new FblthpTheLostTargetedTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - MageObject eventSourceObject = game.getObject(event.getSourceId()); - if (event.getTargetId().equals(this.getSourceId()) && eventSourceObject instanceof Spell) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - return false; - } - - @Override - public String getRule() { - return "When {this} becomes the target of a spell, shuffle {this} into its owner's library."; - } - -} diff --git a/Mage.Sets/src/mage/cards/f/FellBeastOfMordor.java b/Mage.Sets/src/mage/cards/f/FellBeastOfMordor.java new file mode 100644 index 00000000000..166c5220734 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FellBeastOfMordor.java @@ -0,0 +1,61 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.keyword.DevourAbility; +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.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class FellBeastOfMordor extends CardImpl { + + private static final DynamicValue xValue = new CountersSourceCount(CounterType.P1P1); + + public FellBeastOfMordor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + + this.subtype.add(SubType.DRAKE); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Devour 1 + this.addAbility(new DevourAbility(1)); + + // Whenever Fell Beast of Mordor enters the battlefield or attacks, target opponent loses X life and you gain X life, where X is the number of +1/+1 counters on it. + TriggeredAbility trigger = new EntersBattlefieldOrAttacksSourceTriggeredAbility( + new LoseLifeTargetEffect(xValue) + .setText("target opponent loses X life") + ); + trigger.addEffect(new GainLifeEffect(xValue) + .setText(" and you gain X life, where X is the number of +1/+1 counters on it")); + trigger.addTarget(new TargetOpponent()); + this.addAbility(trigger); + } + + private FellBeastOfMordor(final FellBeastOfMordor card) { + super(card); + } + + @Override + public FellBeastOfMordor copy() { + return new FellBeastOfMordor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FeySteed.java b/Mage.Sets/src/mage/cards/f/FeySteed.java index 9618e16beec..beaecd6abeb 100644 --- a/Mage.Sets/src/mage/cards/f/FeySteed.java +++ b/Mage.Sets/src/mage/cards/f/FeySteed.java @@ -1,28 +1,24 @@ package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.IndestructibleAbility; -import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.filter.predicate.permanent.AttackingPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCreaturePermanent; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; /** * @@ -30,13 +26,18 @@ import mage.constants.Duration; */ public final class FeySteed extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent( - "another target attacking creature you control"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another target attacking creature you control"); + private static final FilterControlledPermanent filter2 = new FilterControlledPermanent("a creature or planeswalker you control"); static { filter.add(TargetController.YOU.getControllerPredicate()); filter.add(AnotherPredicate.instance); filter.add(AttackingPredicate.instance); + + filter2.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.PLANESWALKER.getPredicate() + )); } public FeySteed(UUID ownerId, CardSetInfo setInfo) { @@ -46,16 +47,15 @@ public final class FeySteed extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // Whenever Fey Steed attacks, another target attacking creature you control - // gains indestructible until end of turn. + // Whenever Fey Steed attacks, another target attacking creature you control gains indestructible until end of turn. Ability ability = new AttacksTriggeredAbility( new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), false); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); - // Whenever a creature or planeswalker you control becomes the target of a spell - // or ability an opponent controls, you may draw a card. - this.addAbility(new FeySteedTriggeredAbility()); + // Whenever a creature or planeswalker you control becomes the target of a spell or ability an opponent controls, you may draw a card. + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DrawCardSourceControllerEffect(1), + filter2, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true)); } private FeySteed(final FeySteed card) { @@ -67,44 +67,3 @@ public final class FeySteed extends CardImpl { return new FeySteed(this); } } - -class FeySteedTriggeredAbility extends TriggeredAbilityImpl { - - public FeySteedTriggeredAbility() { - super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), true); - } - - private FeySteedTriggeredAbility(final FeySteedTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - 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()) - || (!permanent.isCreature(game) && !permanent.isPlaneswalker(game))) { - return false; - } - return true; - } - - @Override - public String getRule() { - return "Whenever a creature or planeswalker you control becomes the target of a spell or ability an opponent controls, you may draw a card."; - } - - @Override - public FeySteedTriggeredAbility copy() { - return new FeySteedTriggeredAbility(this); - } -} diff --git a/Mage.Sets/src/mage/cards/f/FiendslayerPaladin.java b/Mage.Sets/src/mage/cards/f/FiendslayerPaladin.java index 4927cf4b108..031e1798c43 100644 --- a/Mage.Sets/src/mage/cards/f/FiendslayerPaladin.java +++ b/Mage.Sets/src/mage/cards/f/FiendslayerPaladin.java @@ -72,11 +72,6 @@ class FiendslayerPaladinEffect extends ContinuousRuleModifyingEffectImpl { return new FiendslayerPaladinEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/f/Flamebreak.java b/Mage.Sets/src/mage/cards/f/Flamebreak.java index 41b11a6c132..566b661a8fd 100644 --- a/Mage.Sets/src/mage/cards/f/Flamebreak.java +++ b/Mage.Sets/src/mage/cards/f/Flamebreak.java @@ -1,7 +1,6 @@ package mage.cards.f; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; @@ -20,8 +19,9 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.watchers.common.DamagedByWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class Flamebreak extends CardImpl { @@ -33,7 +33,7 @@ public final class Flamebreak extends CardImpl { } public Flamebreak(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{R}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}{R}{R}"); // Flamebreak deals 3 damage to each creature without flying and each player. Creatures dealt damage this way can't be regenerated this turn. @@ -69,19 +69,14 @@ class FlamebreakCantRegenerateEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - return true; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.REGENERATE; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.REGENERATE) { - DamagedByWatcher watcher = game.getState().getWatcher(DamagedByWatcher.class, source.getSourceId()); - if (watcher != null) { - return watcher.wasDamaged(event.getTargetId(), game); - } - } - return false; + DamagedByWatcher watcher = game.getState().getWatcher(DamagedByWatcher.class, source.getSourceId()); + return watcher != null && watcher.wasDamaged(event.getTargetId(), game); } } diff --git a/Mage.Sets/src/mage/cards/f/FlamescrollCelebrant.java b/Mage.Sets/src/mage/cards/f/FlamescrollCelebrant.java index 79f3acd7c13..040e3bafd19 100644 --- a/Mage.Sets/src/mage/cards/f/FlamescrollCelebrant.java +++ b/Mage.Sets/src/mage/cards/f/FlamescrollCelebrant.java @@ -127,11 +127,6 @@ class RevelInSilenceEffect extends ContinuousRuleModifyingEffectImpl { 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()); diff --git a/Mage.Sets/src/mage/cards/f/FollowedFootsteps.java b/Mage.Sets/src/mage/cards/f/FollowedFootsteps.java index 22272087872..e453cd8474b 100644 --- a/Mage.Sets/src/mage/cards/f/FollowedFootsteps.java +++ b/Mage.Sets/src/mage/cards/f/FollowedFootsteps.java @@ -13,7 +13,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -71,12 +70,7 @@ class FollowedFootstepsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // In the case that Followed Footsteps is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment != null) { Permanent target = game.getPermanentOrLKIBattlefield(enchantment.getAttachedTo()); if (target != null) { diff --git a/Mage.Sets/src/mage/cards/f/ForceProjection.java b/Mage.Sets/src/mage/cards/f/ForceProjection.java index 511431b16f5..b6906c821ab 100644 --- a/Mage.Sets/src/mage/cards/f/ForceProjection.java +++ b/Mage.Sets/src/mage/cards/f/ForceProjection.java @@ -2,7 +2,7 @@ package mage.cards.f; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; import mage.abilities.effects.common.SacrificeSourceEffect; @@ -73,10 +73,10 @@ class ForceProjectionEffect extends OneShotEffect { effect.setTargetPointer(new FixedTarget(permanent, game)); // except that it is an Illusion in addition to its other types - effect.setAdditionalSubType(SubType.SPIRIT); + effect.withAdditionalSubType(SubType.SPIRIT); // and gains "When this creature becomes the target of a spell, sacrifice it." - effect.addAdditionalAbilities(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect(), new FilterSpell())); + effect.addAdditionalAbilities(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect(), new FilterSpell())); return effect.apply(game, source); } diff --git a/Mage.Sets/src/mage/cards/f/FormOfTheSquirrel.java b/Mage.Sets/src/mage/cards/f/FormOfTheSquirrel.java index dd561e5c0fb..57d2468289e 100644 --- a/Mage.Sets/src/mage/cards/f/FormOfTheSquirrel.java +++ b/Mage.Sets/src/mage/cards/f/FormOfTheSquirrel.java @@ -111,12 +111,12 @@ class FormOfTheSquirrelCantCastEffect extends ContinuousRuleModifyingEffectImpl } @Override - public boolean apply(Game game, Ability source) { - return true; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CAST_SPELL; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - return event.getType() == GameEvent.EventType.CAST_SPELL && event.getPlayerId().equals(source.getControllerId()); + return event.getPlayerId().equals(source.getControllerId()); } } diff --git a/Mage.Sets/src/mage/cards/f/ForsakenWastes.java b/Mage.Sets/src/mage/cards/f/ForsakenWastes.java index 82d7e4b19a0..2f03c32a4e4 100644 --- a/Mage.Sets/src/mage/cards/f/ForsakenWastes.java +++ b/Mage.Sets/src/mage/cards/f/ForsakenWastes.java @@ -1,28 +1,19 @@ - package mage.cards.f; -import java.util.UUID; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.continuous.CantGainLifeAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SuperType; -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.game.stack.Spell; -import mage.target.targetpointer.FixedTarget; +import mage.constants.*; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author fireshoes + * @author xenohedron */ public final class ForsakenWastes extends CardImpl { @@ -31,13 +22,15 @@ public final class ForsakenWastes extends CardImpl { this.supertype.add(SuperType.WORLD); // Players can't gain life. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantGainLifeAllEffect())); + this.addAbility(new SimpleStaticAbility(new CantGainLifeAllEffect())); // At the beginning of each player's upkeep, that player loses 1 life. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(1), TargetController.ANY, false, true)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(1), + TargetController.ANY, false, true)); // Whenever Forsaken Wastes becomes the target of a spell, that spell's controller loses 5 life. - this.addAbility(new ForsakenWastesTriggeredAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new LoseLifeTargetEffect(5).setText("that spell's controller loses 5 life"), + StaticFilters.FILTER_SPELL_A, SetTargetPointer.PLAYER, false)); } private ForsakenWastes(final ForsakenWastes card) { @@ -49,40 +42,3 @@ public final class ForsakenWastes extends CardImpl { return new ForsakenWastes(this); } } - -class ForsakenWastesTriggeredAbility extends TriggeredAbilityImpl { - - public ForsakenWastesTriggeredAbility() { - super(Zone.BATTLEFIELD, new LoseLifeTargetEffect(5), false); - } - - private ForsakenWastesTriggeredAbility(final ForsakenWastesTriggeredAbility ability) { - super(ability); - } - - @Override - public ForsakenWastesTriggeredAbility copy() { - return new ForsakenWastesTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - MageObject eventSourceObject = game.getObject(event.getSourceId()); - if (event.getTargetId().equals(this.getSourceId()) && eventSourceObject instanceof Spell) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell, that spell's controller loses 5 life."; - } - -} diff --git a/Mage.Sets/src/mage/cards/f/FracturedLoyalty.java b/Mage.Sets/src/mage/cards/f/FracturedLoyalty.java index 7e4cebd0d2f..6270e2f7ca7 100644 --- a/Mage.Sets/src/mage/cards/f/FracturedLoyalty.java +++ b/Mage.Sets/src/mage/cards/f/FracturedLoyalty.java @@ -2,7 +2,7 @@ package mage.cards.f; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAttachedTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; @@ -11,8 +11,8 @@ import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +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; @@ -37,7 +37,9 @@ public final class FracturedLoyalty extends CardImpl { this.addAbility(ability); // Whenever enchanted creature becomes the target of a spell or ability, that spell or ability's controller gains control of that creature. - this.addAbility(new FracturedLoyaltyTriggeredAbility()); + this.addAbility(new BecomesTargetAttachedTriggeredAbility(new FracturedLoyaltyEffect(), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.PLAYER, false) + .setTriggerPhrase("Whenever enchanted creature becomes the target of a spell or ability, ")); } private FracturedLoyalty(final FracturedLoyalty card) { @@ -49,85 +51,42 @@ public final class FracturedLoyalty extends CardImpl { return new FracturedLoyalty(this); } - private static class FracturedLoyaltyEffect extends OneShotEffect { - - FracturedLoyaltyEffect() { - super(Outcome.GainControl); - this.staticText = "that spell or ability's controller gains control of that creature"; - } - - private FracturedLoyaltyEffect(final FracturedLoyaltyEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - // In the case that Fractured Loyalty is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } - if (enchantment != null) { - Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo()); - if (enchantedCreature != null) { - Player controller = game.getPlayer(enchantedCreature.getControllerId()); - if (enchantment.getAttachedTo() != null) { - if (controller != null && !enchantedCreature.isControlledBy(this.getTargetPointer().getFirst(game, source))) { - ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame, this.getTargetPointer().getFirst(game, source)); - effect.setTargetPointer(new FixedTarget(enchantment.getAttachedTo(), game)); - game.addEffect(effect, source); - return true; - } - } - } - } - return false; - } - - @Override - public FracturedLoyaltyEffect copy() { - return new FracturedLoyaltyEffect(this); - } - - } - - class FracturedLoyaltyTriggeredAbility extends TriggeredAbilityImpl { - - public FracturedLoyaltyTriggeredAbility() { - super(Zone.BATTLEFIELD, new FracturedLoyaltyEffect(), false); - } - - private FracturedLoyaltyTriggeredAbility(final FracturedLoyaltyTriggeredAbility ability) { - super(ability); - } - - @Override - public FracturedLoyaltyTriggeredAbility copy() { - return new FracturedLoyaltyTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent enchantment = game.getPermanentOrLKIBattlefield(this.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo()); - if (enchantedCreature != null && event.getTargetId().equals(enchantment.getAttachedTo())) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever enchanted creature becomes the target of a spell or ability, that spell or ability's controller gains control of that creature."; - } - } +} + +class FracturedLoyaltyEffect extends OneShotEffect { + + FracturedLoyaltyEffect() { + super(Outcome.GainControl); + this.staticText = "that spell or ability's controller gains control of that creature"; + } + + private FracturedLoyaltyEffect(final FracturedLoyaltyEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent enchantment = source.getSourcePermanentOrLKI(game); + if (enchantment == null || enchantment.getAttachedTo() == null) { + return false; + } + Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo()); + if (enchantedCreature == null) { + return false; + } + Player controller = game.getPlayer(enchantedCreature.getControllerId()); + if (controller != null && !enchantedCreature.isControlledBy(this.getTargetPointer().getFirst(game, source))) { + ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame, this.getTargetPointer().getFirst(game, source)); + effect.setTargetPointer(new FixedTarget(enchantment.getAttachedTo(), game)); + game.addEffect(effect, source); + return true; + } + return false; + } + + @Override + public FracturedLoyaltyEffect copy() { + return new FracturedLoyaltyEffect(this); + } + } diff --git a/Mage.Sets/src/mage/cards/f/FrayingSanity.java b/Mage.Sets/src/mage/cards/f/FrayingSanity.java index 8413826df59..5d749e0185f 100644 --- a/Mage.Sets/src/mage/cards/f/FrayingSanity.java +++ b/Mage.Sets/src/mage/cards/f/FrayingSanity.java @@ -14,7 +14,6 @@ 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; @@ -86,8 +85,6 @@ class FrayingSanityTriggeredAbility extends TriggeredAbilityImpl { class FrayingSanityEffect extends OneShotEffect { - int xAmount = 0; - public FrayingSanityEffect() { super(Outcome.Detriment); this.staticText = ""; @@ -104,21 +101,14 @@ class FrayingSanityEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment == null) { return false; } Player enchantedPlayer = game.getPlayer(enchantment.getAttachedTo()); if (enchantedPlayer != null) { CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); - if (watcher != null) { - xAmount = watcher.getAmountCardsPutToGraveyard(enchantedPlayer.getId()); - } + int xAmount = (watcher == null) ? 0 : watcher.getAmountCardsPutToGraveyard(enchantedPlayer.getId()); enchantedPlayer.millCards(xAmount, source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/f/FrostTitan.java b/Mage.Sets/src/mage/cards/f/FrostTitan.java index ca75d0dcd1d..1647ffe9ad2 100644 --- a/Mage.Sets/src/mage/cards/f/FrostTitan.java +++ b/Mage.Sets/src/mage/cards/f/FrostTitan.java @@ -1,11 +1,9 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; @@ -13,13 +11,12 @@ import mage.abilities.effects.common.TapTargetEffect; 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.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; -import mage.target.TargetStackObject; + +import java.util.UUID; /** * @@ -35,7 +32,10 @@ public final class FrostTitan extends CardImpl { this.toughness = new MageInt(6); // Whenever Frost Titan becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays 2. - this.addAbility(new FrostTitanAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility( + new CounterUnlessPaysEffect(new GenericManaCost(2)).setText("counter that spell or ability unless its controller pays {2}"), + StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false + )); // Whenever Frost Titan enters the battlefield or attacks, tap target permanent. It doesn't untap during its controller's next untap step. Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new TapTargetEffect()); @@ -54,42 +54,3 @@ public final class FrostTitan extends CardImpl { } } - -class FrostTitanAbility extends TriggeredAbilityImpl { - - public FrostTitanAbility() { - super(Zone.BATTLEFIELD, new CounterUnlessPaysEffect(new GenericManaCost(2)), false); - } - - private FrostTitanAbility(final FrostTitanAbility ability) { - super(ability); - } - - @Override - public FrostTitanAbility copy() { - return new FrostTitanAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - this.getTargets().clear(); - TargetStackObject target = new TargetStackObject(); - target.add(event.getSourceId(), game); - this.addTarget(target); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {2}."; - } - -} diff --git a/Mage.Sets/src/mage/cards/f/FrostWalker.java b/Mage.Sets/src/mage/cards/f/FrostWalker.java index b144919055d..e4567b79d1c 100644 --- a/Mage.Sets/src/mage/cards/f/FrostWalker.java +++ b/Mage.Sets/src/mage/cards/f/FrostWalker.java @@ -3,7 +3,7 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +23,7 @@ public final class FrostWalker extends CardImpl { this.toughness = new MageInt(1); // When Frost Walker becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private FrostWalker(final FrostWalker card) { diff --git a/Mage.Sets/src/mage/cards/f/FugitiveDruid.java b/Mage.Sets/src/mage/cards/f/FugitiveDruid.java index 69da20040fb..0bd4df57998 100644 --- a/Mage.Sets/src/mage/cards/f/FugitiveDruid.java +++ b/Mage.Sets/src/mage/cards/f/FugitiveDruid.java @@ -3,13 +3,14 @@ package mage.cards.f; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterSpell; +import mage.filter.predicate.other.AuraSpellPredicate; /** * @@ -20,7 +21,7 @@ public final class FugitiveDruid extends CardImpl { private static final FilterSpell filter = new FilterSpell("an Aura spell"); static { - filter.add(SubType.AURA.getPredicate()); + filter.add(AuraSpellPredicate.instance); } public FugitiveDruid(UUID ownerId, CardSetInfo setInfo) { @@ -31,7 +32,7 @@ public final class FugitiveDruid extends CardImpl { this.toughness = new MageInt(2); // Whenever Fugitive Druid becomes the target of an Aura spell, you draw a card. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new DrawCardSourceControllerEffect(1), filter)); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new DrawCardSourceControllerEffect(1, "you"), filter)); } private FugitiveDruid(final FugitiveDruid card) { diff --git a/Mage.Sets/src/mage/cards/g/GaddockTeeg.java b/Mage.Sets/src/mage/cards/g/GaddockTeeg.java index 7b929a45fd6..c84d4251505 100644 --- a/Mage.Sets/src/mage/cards/g/GaddockTeeg.java +++ b/Mage.Sets/src/mage/cards/g/GaddockTeeg.java @@ -55,11 +55,6 @@ class GaddockTeegReplacementEffect4 extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public GaddockTeegReplacementEffect4 copy() { return new GaddockTeegReplacementEffect4(this); @@ -92,11 +87,6 @@ class GaddockTeegReplacementEffectX extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public GaddockTeegReplacementEffectX copy() { return new GaddockTeegReplacementEffectX(this); diff --git a/Mage.Sets/src/mage/cards/g/GaeasHerald.java b/Mage.Sets/src/mage/cards/g/GaeasHerald.java index ce0cc8c22c1..8b1e7958743 100644 --- a/Mage.Sets/src/mage/cards/g/GaeasHerald.java +++ b/Mage.Sets/src/mage/cards/g/GaeasHerald.java @@ -62,11 +62,6 @@ class CantCounterEffect extends ContinuousRuleModifyingEffectImpl { return new CantCounterEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.COUNTER; diff --git a/Mage.Sets/src/mage/cards/g/GaladhrimBrigade.java b/Mage.Sets/src/mage/cards/g/GaladhrimBrigade.java new file mode 100644 index 00000000000..b2fc1e490ac --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GaladhrimBrigade.java @@ -0,0 +1,49 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.keyword.SquadAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class GaladhrimBrigade extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.ELF, "Elves"); + + public GaladhrimBrigade(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Squad {1}{G} + this.addAbility(new SquadAbility(new ManaCostsImpl<>("{1}{G}"))); + + // Other Elves you control get +1/+1. + this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter, true + ))); + } + + private GaladhrimBrigade(final GaladhrimBrigade card) { + super(card); + } + + @Override + public GaladhrimBrigade copy() { + return new GaladhrimBrigade(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GaladrielLightOfValinor.java b/Mage.Sets/src/mage/cards/g/GaladrielLightOfValinor.java new file mode 100644 index 00000000000..0bfca2a3537 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GaladrielLightOfValinor.java @@ -0,0 +1,67 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.AllianceAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.effects.mana.AddManaToManaPoolSourceControllerEffect; +import mage.abilities.hint.common.ModesAlreadyUsedHint; +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 java.util.UUID; + +/** + * @author Susucr + */ +public final class GaladrielLightOfValinor extends CardImpl { + + public GaladrielLightOfValinor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Alliance — Whenever another creature enters the battlefield under your control, choose one that hasn't been chosen this turn — + // • Add {G}{G}{G}. + Ability ability = new AllianceAbility(new AddManaToManaPoolSourceControllerEffect(Mana.GreenMana(3))); + ability.setModeTag("add mana"); + ability.getModes().setEachModeOnlyOnce(true); + ability.getModes().setResetEachTurn(true); + + // • Put a +1/+1 counter on each creature you control. + ability.addMode( + new Mode(new AddCountersAllEffect(CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE)) + .setModeTag("put +1/+1 counters") + ); + + // • Scry 2, then draw a card. + Mode mode = new Mode(new ScryEffect(2, false)).setModeTag("scry and draw"); + mode.addEffect(new DrawCardSourceControllerEffect(1).concatBy(", then")); + ability.addMode(mode); + + ability.addHint(ModesAlreadyUsedHint.instance); + this.addAbility(ability); + } + + private GaladrielLightOfValinor(final GaladrielLightOfValinor card) { + super(card); + } + + @Override + public GaladrielLightOfValinor copy() { + return new GaladrielLightOfValinor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GargosViciousWatcher.java b/Mage.Sets/src/mage/cards/g/GargosViciousWatcher.java index 0969dc07e4e..40e324de19b 100644 --- a/Mage.Sets/src/mage/cards/g/GargosViciousWatcher.java +++ b/Mage.Sets/src/mage/cards/g/GargosViciousWatcher.java @@ -1,8 +1,8 @@ package mage.cards.g; import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.FightTargetSourceEffect; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; @@ -10,16 +10,12 @@ import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; -import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -48,9 +44,12 @@ public final class GargosViciousWatcher extends CardImpl { // Hydra spells you cast cost {4} less to cast. this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 4))); - // Whenever a creature you control becomes the target of a spell, Gargos, - // Vicious Watcher fights up to one target creature you don't control. - this.addAbility(new GargosViciousWatcherTriggeredAbility()); + // Whenever a creature you control becomes the target of a spell, Gargos, Vicious Watcher fights up to one target creature you don't control. + TriggeredAbility ability = new BecomesTargetAnyTriggeredAbility(new FightTargetSourceEffect(), + StaticFilters.FILTER_CONTROLLED_A_CREATURE, StaticFilters.FILTER_SPELL_A, + SetTargetPointer.NONE, false); + ability.addTarget(new TargetCreaturePermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); + this.addAbility(ability); } private GargosViciousWatcher(final GargosViciousWatcher card) { @@ -62,44 +61,3 @@ public final class GargosViciousWatcher extends CardImpl { return new GargosViciousWatcher(this); } } - -class GargosViciousWatcherTriggeredAbility extends TriggeredAbilityImpl { - - GargosViciousWatcherTriggeredAbility() { - super(Zone.BATTLEFIELD, new FightTargetSourceEffect()); - this.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); - } - - private GargosViciousWatcherTriggeredAbility(final GargosViciousWatcherTriggeredAbility ability) { - super(ability); - } - - @Override - public GargosViciousWatcherTriggeredAbility copy() { - return new GargosViciousWatcherTriggeredAbility(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.getPermanent(event.getTargetId()); - MageObject object = game.getObject(event.getSourceId()); - if (permanent == null - || object == null - || !permanent.isControlledBy(this.controllerId) - || !permanent.isCreature(game)) { - return false; - } - return object instanceof Spell; // must be a type of spell (instant, sorcery, or aura) - } - - @Override - public String getRule() { - return "Whenever a creature you control becomes the target of a spell, " - + "{this} fights up to one target creature you don't control."; - } -} diff --git a/Mage.Sets/src/mage/cards/g/GenestealerPatriarch.java b/Mage.Sets/src/mage/cards/g/GenestealerPatriarch.java index 24ae47a2b65..7fcd21a6518 100644 --- a/Mage.Sets/src/mage/cards/g/GenestealerPatriarch.java +++ b/Mage.Sets/src/mage/cards/g/GenestealerPatriarch.java @@ -2,7 +2,6 @@ package mage.cards.g; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -13,18 +12,14 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.DefendingPlayerControlsSourceAttackingPredicate; 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.TargetPermanent; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -73,44 +68,6 @@ public final class GenestealerPatriarch extends CardImpl { } } -class GenestealerPatriarchTriggeredAbility extends TriggeredAbilityImpl { - - public GenestealerPatriarchTriggeredAbility() { - super(Zone.BATTLEFIELD, new GenestealerPatriarchCloneEffect()); - setTriggerPhrase("Whenever a creature with an infection counter on it dies, "); - ; - } - - private GenestealerPatriarchTriggeredAbility(final GenestealerPatriarchTriggeredAbility ability) { - super(ability); - } - - @Override - public GenestealerPatriarchTriggeredAbility copy() { - return new GenestealerPatriarchTriggeredAbility(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 = game.getPermanentOrLKIBattlefield(zEvent.getTargetId()); - if (permanent != null - && permanent.isCreature(game) - && permanent.getCounters(game).containsKey(CounterType.INFECTION)) { - this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); - return true; - } - } - return false; - } -} - class GenestealerPatriarchCloneEffect extends OneShotEffect { public GenestealerPatriarchCloneEffect() { @@ -137,7 +94,7 @@ class GenestealerPatriarchCloneEffect extends OneShotEffect { } CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId()); effect.setSavedPermanent(creature); - effect.setAdditionalSubType(SubType.TYRANID); + effect.withAdditionalSubType(SubType.TYRANID); return effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/g/GhastlyMimicry.java b/Mage.Sets/src/mage/cards/g/GhastlyMimicry.java index 054d3a5bf29..b8a0b6497d9 100644 --- a/Mage.Sets/src/mage/cards/g/GhastlyMimicry.java +++ b/Mage.Sets/src/mage/cards/g/GhastlyMimicry.java @@ -88,7 +88,7 @@ class GhastlyMimicryEffect extends OneShotEffect { return false; } CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(); - effect.setAdditionalSubType(SubType.SPIRIT); + effect.withAdditionalSubType(SubType.SPIRIT); return effect.setTargetPointer(new FixedTarget(attached, game)).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/g/GlimmerLens.java b/Mage.Sets/src/mage/cards/g/GlimmerLens.java index 46d2d3375f8..e3210ab50d0 100644 --- a/Mage.Sets/src/mage/cards/g/GlimmerLens.java +++ b/Mage.Sets/src/mage/cards/g/GlimmerLens.java @@ -71,6 +71,9 @@ class GlimmerLensTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent attachment = getSourcePermanentOrLKI(game); + if (attachment == null) { + return false; + } UUID equippedCreature = attachment.getAttachedTo(); return game.getCombat().getAttackers().contains(equippedCreature) && game diff --git a/Mage.Sets/src/mage/cards/g/GlyphKeeper.java b/Mage.Sets/src/mage/cards/g/GlyphKeeper.java index 2644ded03be..5572b1f7ec4 100644 --- a/Mage.Sets/src/mage/cards/g/GlyphKeeper.java +++ b/Mage.Sets/src/mage/cards/g/GlyphKeeper.java @@ -1,28 +1,23 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.keyword.EmbalmAbility; import mage.abilities.keyword.FlyingAbility; 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.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * - * @author Styxo + * @author Styxo, xenohedron */ public final class GlyphKeeper extends CardImpl { @@ -37,7 +32,10 @@ public final class GlyphKeeper extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Glyph Keeper becomes the target of a spell or ability for the first time each turn, counter that spell or ability. - this.addAbility(new GlyphKeeperAbility(), new NumberOfTimesPermanentTargetedATurnWatcher()); + this.addAbility(new BecomesTargetSourceFirstTimeTriggeredAbility( + new CounterTargetEffect().setText("counter that spell or ability"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.SPELL, false + )); // Embalm {5}{U}{U} this.addAbility(new EmbalmAbility(new ManaCostsImpl<>("{5}{U}{U}"), this)); @@ -53,48 +51,3 @@ public final class GlyphKeeper extends CardImpl { return new GlyphKeeper(this); } } - -class GlyphKeeperAbility extends TriggeredAbilityImpl { - - public GlyphKeeperAbility() { - super(Zone.BATTLEFIELD, new CounterTargetEffect(), false); - } - - private GlyphKeeperAbility(final GlyphKeeperAbility ability) { - super(ability); - } - - @Override - public GlyphKeeperAbility copy() { - return new GlyphKeeperAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature(game)) { - NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); - if (watcher != null - && watcher.notMoreThanOnceTargetedThisTurn(permanent, game)) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); - } - return true; - } - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability for the first time each turn, counter that spell or ability."; - } - -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GoblinRockSled.java b/Mage.Sets/src/mage/cards/g/GoblinRockSled.java index 67909d45b1d..9ad75e89964 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinRockSled.java +++ b/Mage.Sets/src/mage/cards/g/GoblinRockSled.java @@ -79,11 +79,6 @@ class DontUntapIfAttackedLastTurnSourceEffect extends ContinuousRuleModifyingEff return new DontUntapIfAttackedLastTurnSourceEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.UNTAP; diff --git a/Mage.Sets/src/mage/cards/g/Godsend.java b/Mage.Sets/src/mage/cards/g/Godsend.java index ce8ec73e5da..c068fed8257 100644 --- a/Mage.Sets/src/mage/cards/g/Godsend.java +++ b/Mage.Sets/src/mage/cards/g/Godsend.java @@ -188,9 +188,14 @@ class GodsendRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl { return null; } + @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 (event.getType() == GameEvent.EventType.CAST_SPELL && game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { + if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { MageObject object = game.getObject(event.getSourceId()); if (object != null) { ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); diff --git a/Mage.Sets/src/mage/cards/g/GoldspanDragon.java b/Mage.Sets/src/mage/cards/g/GoldspanDragon.java index 5347db525ac..ccd15cd7c68 100644 --- a/Mage.Sets/src/mage/cards/g/GoldspanDragon.java +++ b/Mage.Sets/src/mage/cards/g/GoldspanDragon.java @@ -1,9 +1,9 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.SacrificeSourceCost; @@ -11,21 +11,21 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.effects.mana.AddManaOfAnyColorEffect; -import mage.abilities.mana.SimpleManaAbility; -import mage.constants.Duration; -import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.meta.OrTriggeredAbility; 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.FilterPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; +import mage.filter.StaticFilters; import mage.game.permanent.token.TreasureToken; -import mage.game.stack.Spell; -import mage.game.stack.StackObject; + +import java.util.UUID; /** * @@ -49,7 +49,10 @@ public final class GoldspanDragon extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Whenever Goldspan Dragon attacks or becomes the target of a spell, create a Treasure token. - this.addAbility(new GoldspanDragonTriggeredAbility()); + this.addAbility(new OrTriggeredAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new TreasureToken()), false, + "Whenever {this} attacks or becomes the target of a spell, ", + new AttacksTriggeredAbility(null), + new BecomesTargetSourceTriggeredAbility(null, StaticFilters.FILTER_SPELL_A))); // Treasures you control have "{T}, Sacrifice this artifact: Add two mana of any one color." Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(2), new TapSourceCost()); @@ -68,41 +71,3 @@ public final class GoldspanDragon extends CardImpl { return new GoldspanDragon(this); } } - -class GoldspanDragonTriggeredAbility extends TriggeredAbilityImpl { - - GoldspanDragonTriggeredAbility() { - super(Zone.BATTLEFIELD, new CreateTokenEffect(new TreasureToken())); - setTriggerPhrase("Whenever {this} attacks or becomes the target of a spell, "); - } - - private GoldspanDragonTriggeredAbility(final GoldspanDragonTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS - || event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - switch (event.getType()) { - case DECLARED_ATTACKERS: - return game.getCombat().getAttackers().contains(this.getSourceId()); - case TARGETED: - if (event.getTargetId().equals(getSourceId())) { - StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); - return sourceObject instanceof Spell; - } - default: - return false; - } - } - - @Override - public GoldspanDragonTriggeredAbility copy() { - return new GoldspanDragonTriggeredAbility(this); - } -} diff --git a/Mage.Sets/src/mage/cards/g/GolgothianSylex.java b/Mage.Sets/src/mage/cards/g/GolgothianSylex.java index bca8a8752e0..beae1453196 100644 --- a/Mage.Sets/src/mage/cards/g/GolgothianSylex.java +++ b/Mage.Sets/src/mage/cards/g/GolgothianSylex.java @@ -1,6 +1,8 @@ - package mage.cards.g; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -14,7 +16,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.card.ExpansionSetPredicate; +import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -49,13 +51,17 @@ class GolgothianSylexEffect extends OneShotEffect { private static final FilterPermanent filter = new FilterPermanent(); static { - filter.add(Predicates.and( - new ExpansionSetPredicate("ATQ"), - TokenPredicate.FALSE - )); + // Antiquities names per CR 206.3b + List nameStrings = Arrays.asList("Amulet of Kroog", "Argivian Archaeologist", "Argivian Blacksmith", "Argothian Pixies", "Argothian Treefolk", "Armageddon Clock", "Artifact Blast", "Artifact Possession", "Artifact Ward", "Ashnod's Altar", "Ashnod's Battle Gear", "Ashnod's Transmogrant", "Atog", "Battering Ram", "Bronze Tablet", "Candelabra of Tawnos", "Circle of Protection: Artifacts", "Citanul Druid", "Clay Statue", "Clockwork Avian", "Colossus of Sardia", "Coral Helm", "Crumble", "Cursed Rack", "Damping Field", "Detonate", "Drafna's Restoration", "Dragon Engine", "Dwarven Weaponsmith", "Energy Flux", "Feldon's Cane", "Gaea's Avenger", "Gate to Phyrexia", "Goblin Artisans", "Golgothian Sylex", "Grapeshot Catapult", "Haunting Wind", "Hurkyl's Recall", "Ivory Tower", "Jalum Tome", "Martyrs of Korlis", "Mightstone", "Millstone", "Mishra's Factory", "Mishra's War Machine", "Mishra's Workshop", "Obelisk of Undoing", "Onulet", "Orcish Mechanics", "Ornithopter", "Phyrexian Gremlins", "Power Artifact", "Powerleech", "Priest of Yawgmoth", "Primal Clay", "The Rack", "Rakalite", "Reconstruction", "Reverse Polarity", "Rocket Launcher", "Sage of Lat-Nam", "Shapeshifter", "Shatterstorm", "Staff of Zegon", "Strip Mine", "Su-Chi", "Tablet of Epityr", "Tawnos's Coffin", "Tawnos's Wand", "Tawnos's Weaponry", "Tetravus", "Titania's Song", "Transmute Artifact", "Triskelion", "Urza's Avenger", "Urza's Chalice", "Urza's Mine", "Urza's Miter", "Urza's Power Plant", "Urza's Tower", "Wall of Spears", "Weakstone", "Xenic Poltergeist", "Yawgmoth Demon", "Yotian Soldier"); + List namePredicates = new ArrayList<>(); + for (String name: nameStrings) { + namePredicates.add(new NamePredicate(name)); + } + filter.add(TokenPredicate.FALSE); + filter.add(Predicates.or(namePredicates)); } - public GolgothianSylexEffect() { + GolgothianSylexEffect() { super(Outcome.Benefit); this.staticText = "Each nontoken permanent from the Antiquities expansion is sacrificed by its controller."; } @@ -72,9 +78,9 @@ class GolgothianSylexEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) { permanent.sacrifice(source, game); } return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/g/GossamerPhantasm.java b/Mage.Sets/src/mage/cards/g/GossamerPhantasm.java index 12377566179..b021f75f549 100644 --- a/Mage.Sets/src/mage/cards/g/GossamerPhantasm.java +++ b/Mage.Sets/src/mage/cards/g/GossamerPhantasm.java @@ -1,7 +1,7 @@ package mage.cards.g; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class GossamerPhantasm extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Gossamer Phantasm becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private GossamerPhantasm(final GossamerPhantasm card) { diff --git a/Mage.Sets/src/mage/cards/g/GridMonitor.java b/Mage.Sets/src/mage/cards/g/GridMonitor.java index 05701914e3a..dfafadc1aba 100644 --- a/Mage.Sets/src/mage/cards/g/GridMonitor.java +++ b/Mage.Sets/src/mage/cards/g/GridMonitor.java @@ -60,13 +60,13 @@ class GridMonitorEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - return true; + 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 (event.getType() == GameEvent.EventType.CAST_SPELL && event.getPlayerId().equals(source.getControllerId())) { + if (event.getPlayerId().equals(source.getControllerId())) { MageObject object = game.getObject(event.getSourceId()); if (object != null && object.isCreature(game)) { return true; diff --git a/Mage.Sets/src/mage/cards/g/GuardianBeast.java b/Mage.Sets/src/mage/cards/g/GuardianBeast.java index 2a4c999bf83..1b4f2d0927c 100644 --- a/Mage.Sets/src/mage/cards/g/GuardianBeast.java +++ b/Mage.Sets/src/mage/cards/g/GuardianBeast.java @@ -87,11 +87,6 @@ class GuardianBeastConditionalEffect extends ContinuousRuleModifyingEffectImpl { return true; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean applies(GameEvent event, Ability source, Game game) { Permanent permanent = source.getSourcePermanentIfItStillExists(game); diff --git a/Mage.Sets/src/mage/cards/h/HaakonStromgaldScourge.java b/Mage.Sets/src/mage/cards/h/HaakonStromgaldScourge.java index 43acbb5f874..fbe88336e61 100644 --- a/Mage.Sets/src/mage/cards/h/HaakonStromgaldScourge.java +++ b/Mage.Sets/src/mage/cards/h/HaakonStromgaldScourge.java @@ -103,11 +103,6 @@ class HaakonStromgaldScourgePlayEffect2 extends ContinuousRuleModifyingEffectImp public HaakonStromgaldScourgePlayEffect2 copy() { return new HaakonStromgaldScourgePlayEffect2(this); } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } @Override public boolean checksEventType(GameEvent event, Game game) { diff --git a/Mage.Sets/src/mage/cards/h/HandToHand.java b/Mage.Sets/src/mage/cards/h/HandToHand.java index 19e74642552..3833fadfef6 100644 --- a/Mage.Sets/src/mage/cards/h/HandToHand.java +++ b/Mage.Sets/src/mage/cards/h/HandToHand.java @@ -53,11 +53,6 @@ class HandToHandEffect extends ContinuousRuleModifyingEffectImpl { return new HandToHandEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -67,6 +62,12 @@ class HandToHandEffect extends ContinuousRuleModifyingEffectImpl { return null; } + @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.getTurnPhaseType() == TurnPhase.COMBAT) { diff --git a/Mage.Sets/src/mage/cards/h/HauntingImitation.java b/Mage.Sets/src/mage/cards/h/HauntingImitation.java index 67bd5fbe30a..d8237b1d4ba 100644 --- a/Mage.Sets/src/mage/cards/h/HauntingImitation.java +++ b/Mage.Sets/src/mage/cards/h/HauntingImitation.java @@ -86,7 +86,7 @@ class HauntingImitationEffect extends OneShotEffect { source.getControllerId(), null, false, 1, false, false, null, 1, 1, true ); - effect.setAdditionalSubType(SubType.SPIRIT); + effect.withAdditionalSubType(SubType.SPIRIT); for (Card card : cards.getCards(game)) { effect.setSavedPermanent(new PermanentCard(CardUtil.getDefaultCardSideForBattlefield(game, card), source.getControllerId(), game)); effect.apply(game, source); diff --git a/Mage.Sets/src/mage/cards/h/Hecatomb.java b/Mage.Sets/src/mage/cards/h/Hecatomb.java index 4a13b0bdcb0..35ad002bf04 100644 --- a/Mage.Sets/src/mage/cards/h/Hecatomb.java +++ b/Mage.Sets/src/mage/cards/h/Hecatomb.java @@ -43,7 +43,7 @@ public final class Hecatomb extends CardImpl { // When Hecatomb enters the battlefield, sacrifice Hecatomb unless you sacrifice four creatures. this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceUnlessPaysEffect( new SacrificeTargetCost(new TargetControlledPermanent(4, filter2)))) - .setReplaceRuleText(false)); + .withRuleTextReplacement(false)); // Tap an untapped Swamp you control: Hecatomb deals 1 damage to any target. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new TapTargetCost(new TargetControlledPermanent(1, 1, filter, true))); diff --git a/Mage.Sets/src/mage/cards/h/HexgoldHalberd.java b/Mage.Sets/src/mage/cards/h/HexgoldHalberd.java index 7f658cbb4b8..64c50c3941e 100644 --- a/Mage.Sets/src/mage/cards/h/HexgoldHalberd.java +++ b/Mage.Sets/src/mage/cards/h/HexgoldHalberd.java @@ -19,7 +19,6 @@ import mage.abilities.keyword.ForMirrodinAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import org.checkerframework.checker.units.qual.A; /** * @author TheElk801 diff --git a/Mage.Sets/src/mage/cards/h/HighAlert.java b/Mage.Sets/src/mage/cards/h/HighAlert.java index cecefcc4408..764a3f00699 100644 --- a/Mage.Sets/src/mage/cards/h/HighAlert.java +++ b/Mage.Sets/src/mage/cards/h/HighAlert.java @@ -6,7 +6,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderAllEffect; -import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessAllEffect; +import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -27,7 +27,7 @@ public final class HighAlert extends CardImpl { // Each creature you control assigns combat damage equal to its toughness rather than its power. - this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_EACH))); + this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessControlledEffect())); // Creatures you control can attack as though they didn't have defender. this.addAbility(new SimpleStaticAbility( diff --git a/Mage.Sets/src/mage/cards/h/HofriGhostforge.java b/Mage.Sets/src/mage/cards/h/HofriGhostforge.java index b2a1c708343..1ef5d0e009b 100644 --- a/Mage.Sets/src/mage/cards/h/HofriGhostforge.java +++ b/Mage.Sets/src/mage/cards/h/HofriGhostforge.java @@ -109,7 +109,7 @@ class HofriGhostforgeEffect extends OneShotEffect { player.moveCards(card, Zone.EXILED, source, game); CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId()); effect.setTargetPointer(new FixedTarget(card, game)); - effect.setAdditionalSubType(SubType.SPIRIT); + effect.withAdditionalSubType(SubType.SPIRIT); effect.addAdditionalAbilities(new ZoneChangeTriggeredAbility( Zone.ALL, Zone.BATTLEFIELD, null, new HofriGhostforgeReturnEffect(card, game), "When this creature leaves the battlefield, ", false diff --git a/Mage.Sets/src/mage/cards/h/HopeAndGlory.java b/Mage.Sets/src/mage/cards/h/HopeAndGlory.java index 1fb33febd76..5beaad2d784 100644 --- a/Mage.Sets/src/mage/cards/h/HopeAndGlory.java +++ b/Mage.Sets/src/mage/cards/h/HopeAndGlory.java @@ -22,7 +22,8 @@ public final class HopeAndGlory extends CardImpl { // Untap two target creatures. Each of them gets +1/+1 until end of turn. this.getSpellAbility().addEffect(new UntapTargetEffect()); - this.getSpellAbility().addEffect(new BoostTargetEffect(1, 1, Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 1, Duration.EndOfTurn) + .setText("Each of them gets +1/+1 until end of turn")); this.getSpellAbility().addTarget(new TargetCreaturePermanent(2)); } diff --git a/Mage.Sets/src/mage/cards/h/HopeOfGhirapur.java b/Mage.Sets/src/mage/cards/h/HopeOfGhirapur.java index 98d16dab170..5d6d73ae2cb 100644 --- a/Mage.Sets/src/mage/cards/h/HopeOfGhirapur.java +++ b/Mage.Sets/src/mage/cards/h/HopeOfGhirapur.java @@ -82,11 +82,6 @@ class HopeOfGhirapurCantCastEffect extends ContinuousRuleModifyingEffectImpl { return new HopeOfGhirapurCantCastEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = source.getSourceObject(game); diff --git a/Mage.Sets/src/mage/cards/h/HorobiDeathsWail.java b/Mage.Sets/src/mage/cards/h/HorobiDeathsWail.java index 267c22f9b85..289d6504247 100644 --- a/Mage.Sets/src/mage/cards/h/HorobiDeathsWail.java +++ b/Mage.Sets/src/mage/cards/h/HorobiDeathsWail.java @@ -1,9 +1,7 @@ package mage.cards.h; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -11,15 +9,12 @@ 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.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author LevelX + * @author xenohedron */ public final class HorobiDeathsWail extends CardImpl { @@ -35,7 +30,7 @@ public final class HorobiDeathsWail extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever a creature becomes the target of a spell or ability, destroy that creature. - this.addAbility(new HorobiDeathsWailAbility(new DestroyTargetEffect())); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DestroyTargetEffect(), StaticFilters.FILTER_PERMANENT_A_CREATURE)); } private HorobiDeathsWail(final HorobiDeathsWail card) { @@ -48,39 +43,3 @@ public final class HorobiDeathsWail extends CardImpl { } } - -class HorobiDeathsWailAbility extends TriggeredAbilityImpl { - - public HorobiDeathsWailAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private HorobiDeathsWailAbility(final HorobiDeathsWailAbility ability) { - super(ability); - } - - @Override - public HorobiDeathsWailAbility copy() { - return new HorobiDeathsWailAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature != null && creature.isCreature(game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId(), game)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature becomes the target of a spell or ability, destroy that creature."; - } -} diff --git a/Mage.Sets/src/mage/cards/h/HuatliTheSunsHeart.java b/Mage.Sets/src/mage/cards/h/HuatliTheSunsHeart.java index 5d4ce5f4caa..d184a7ef852 100644 --- a/Mage.Sets/src/mage/cards/h/HuatliTheSunsHeart.java +++ b/Mage.Sets/src/mage/cards/h/HuatliTheSunsHeart.java @@ -4,13 +4,12 @@ import mage.abilities.LoyaltyAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue; import mage.abilities.effects.common.GainLifeEffect; -import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessAllEffect; +import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.StaticFilters; import java.util.UUID; @@ -27,7 +26,7 @@ public final class HuatliTheSunsHeart extends CardImpl { this.setStartingLoyalty(7); // Each creature you control assigns combat damage equal to its toughness rather than its power. - this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_EACH))); + this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessControlledEffect())); // -3: You gain life equal to the greatest toughness among creatures you control. this.addAbility(new LoyaltyAbility(new GainLifeEffect( diff --git a/Mage.Sets/src/mage/cards/i/IllithidHarvester.java b/Mage.Sets/src/mage/cards/i/IllithidHarvester.java new file mode 100644 index 00000000000..9710d2e4f76 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IllithidHarvester.java @@ -0,0 +1,119 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureAllEffect; +import mage.abilities.effects.common.continuous.BecomesSubtypeAllEffect; +import mage.cards.AdventureCard; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.Arrays; +import java.util.UUID; + +/** + * @author PurpleCrowbar + */ +public final class IllithidHarvester extends AdventureCard { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("tapped nontoken creatures"); + + static { + filter.add(TappedPredicate.TAPPED); + filter.add(TokenPredicate.FALSE); + } + + public IllithidHarvester(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, new CardType[]{CardType.SORCERY}, "{4}{U}", "Plant Tadpoles", "{X}{U}{U}"); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Ceremorphosis — When Illithid Harvester enters the battlefield, turn any number + // of target tapped nontoken creatures face down. They're 2/2 Horror creatures. + Ability ability = new EntersBattlefieldTriggeredAbility(new IllithidHarvesterEffect()); + ability.addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, false)); + this.addAbility(ability.withFlavorWord("Ceremorphosis")); + + // Plant Tadpoles + // Tap X target creatures. They don't untap during their controllers' next untap steps. + this.getSpellCard().getSpellAbility().addEffect(new TapTargetEffect("tap X target creatures")); + this.getSpellCard().getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect() + .setText("They don't untap during their controllers' next untap steps")); + this.getSpellCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellCard().getSpellAbility().setTargetAdjuster(IllithidHarvesterAdjuster.instance); + + this.finalizeAdventure(); + } + + private IllithidHarvester(final IllithidHarvester card) { + super(card); + } + + @Override + public IllithidHarvester copy() { + return new IllithidHarvester(this); + } +} + +enum IllithidHarvesterAdjuster implements TargetAdjuster { + instance; + + @Override + public void adjustTargets(Ability ability, Game game) { + ability.getTargets().clear(); + int xValue = ability.getManaCostsToPay().getX(); + ability.addTarget(new TargetCreaturePermanent(xValue, xValue)); + } +} + +class IllithidHarvesterEffect extends OneShotEffect { + + public IllithidHarvesterEffect() { + super(Outcome.Detriment); + this.staticText = "turn any number of target tapped nontoken creatures face down. They're 2/2 Horror creatures"; + } + + private IllithidHarvesterEffect(final IllithidHarvesterEffect effect) { + super(effect); + } + + @Override + public IllithidHarvesterEffect copy() { + return new IllithidHarvesterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Predicate pred = new PermanentIdPredicate(UUID.randomUUID()); + for (Target target : source.getTargets()) { + for (UUID targetId : target.getTargets()) { + pred = Predicates.or(pred, new PermanentIdPredicate(targetId)); + } + } + FilterCreaturePermanent filter = new FilterCreaturePermanent(); + filter.add(pred); + + game.addEffect(new BecomesFaceDownCreatureAllEffect(filter), source); + game.addEffect(new BecomesSubtypeAllEffect(Duration.WhileOnBattlefield, Arrays.asList(SubType.HORROR), filter, false), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/IlluminatorVirtuoso.java b/Mage.Sets/src/mage/cards/i/IlluminatorVirtuoso.java index 8d35d096209..d6887218a3b 100644 --- a/Mage.Sets/src/mage/cards/i/IlluminatorVirtuoso.java +++ b/Mage.Sets/src/mage/cards/i/IlluminatorVirtuoso.java @@ -1,7 +1,7 @@ package mage.cards.i; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.keyword.ConniveSourceEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.cards.CardImpl; @@ -36,7 +36,7 @@ public final class IlluminatorVirtuoso extends CardImpl { this.addAbility(DoubleStrikeAbility.getInstance()); // Whenever Illuminator Virtuoso becomes the target of a spell you control, it connives. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new ConniveSourceEffect(), filter ).setTriggerPhrase("Whenever {this} becomes the target of a spell you control, ")); } diff --git a/Mage.Sets/src/mage/cards/i/IllusionaryServant.java b/Mage.Sets/src/mage/cards/i/IllusionaryServant.java index fa8b1863d36..817ac856377 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionaryServant.java +++ b/Mage.Sets/src/mage/cards/i/IllusionaryServant.java @@ -3,7 +3,7 @@ package mage.cards.i; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public final class IllusionaryServant extends CardImpl { this.toughness = new MageInt(4); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private IllusionaryServant(final IllusionaryServant card) { diff --git a/Mage.Sets/src/mage/cards/i/Incinerate.java b/Mage.Sets/src/mage/cards/i/Incinerate.java index bda6e4f80d5..1f935b1d327 100644 --- a/Mage.Sets/src/mage/cards/i/Incinerate.java +++ b/Mage.Sets/src/mage/cards/i/Incinerate.java @@ -58,11 +58,6 @@ class IncinerateEffect extends ContinuousRuleModifyingEffectImpl { return new IncinerateEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.REGENERATE; diff --git a/Mage.Sets/src/mage/cards/i/IncubationDruid.java b/Mage.Sets/src/mage/cards/i/IncubationDruid.java index 0bc69a192da..a6206e59dcf 100644 --- a/Mage.Sets/src/mage/cards/i/IncubationDruid.java +++ b/Mage.Sets/src/mage/cards/i/IncubationDruid.java @@ -14,6 +14,7 @@ import mage.cards.CardSetInfo; import mage.choices.Choice; import mage.choices.ChoiceColor; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; @@ -140,9 +141,12 @@ class AnyColorLandsProduceManaEffect extends ManaEffect { } if (!choice.getChoices().isEmpty()) { Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return mana; + } if (choice.getChoices().size() == 1) { choice.setChoice(choice.getChoices().iterator().next()); - } else if (player == null || !player.choose(outcome, choice, game)) { + } else if (!player.choose(Outcome.PutManaInPool, choice, game)) { return mana; } if (choice.getChoice() != null) { diff --git a/Mage.Sets/src/mage/cards/i/Insist.java b/Mage.Sets/src/mage/cards/i/Insist.java index d94448bc4cf..7e408f87b58 100644 --- a/Mage.Sets/src/mage/cards/i/Insist.java +++ b/Mage.Sets/src/mage/cards/i/Insist.java @@ -69,11 +69,6 @@ class InsistEffect extends ContinuousRuleModifyingEffectImpl { } } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject sourceObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/i/InvasionPlans.java b/Mage.Sets/src/mage/cards/i/InvasionPlans.java index a849b31e86a..e6312cb5410 100644 --- a/Mage.Sets/src/mage/cards/i/InvasionPlans.java +++ b/Mage.Sets/src/mage/cards/i/InvasionPlans.java @@ -55,11 +55,6 @@ class InvasionPlansEffect extends ContinuousRuleModifyingEffectImpl { return new InvasionPlansEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS; diff --git a/Mage.Sets/src/mage/cards/i/IronclawCurse.java b/Mage.Sets/src/mage/cards/i/IronclawCurse.java index 32765bee6ab..0d7ba651966 100644 --- a/Mage.Sets/src/mage/cards/i/IronclawCurse.java +++ b/Mage.Sets/src/mage/cards/i/IronclawCurse.java @@ -70,12 +70,7 @@ class IronclawCurseEffect extends CantBlockAttachedEffect { if (attacker == null) { return true; } - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/i/IxalansBinding.java b/Mage.Sets/src/mage/cards/i/IxalansBinding.java index 3fb7615eb17..38138bc80e2 100644 --- a/Mage.Sets/src/mage/cards/i/IxalansBinding.java +++ b/Mage.Sets/src/mage/cards/i/IxalansBinding.java @@ -100,11 +100,6 @@ class IxalansBindingReplacementEffect extends ContinuousRuleModifyingEffectImpl return false; } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public IxalansBindingReplacementEffect copy() { return new IxalansBindingReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/i/IzoniThousandEyed.java b/Mage.Sets/src/mage/cards/i/IzoniThousandEyed.java index cf18f109e06..9624ece5cf9 100644 --- a/Mage.Sets/src/mage/cards/i/IzoniThousandEyed.java +++ b/Mage.Sets/src/mage/cards/i/IzoniThousandEyed.java @@ -11,6 +11,7 @@ import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.hint.ValueHint; import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -42,6 +43,7 @@ public final class IzoniThousandEyed extends CardImpl { ) ), false) .setAbilityWord(AbilityWord.UNDERGROWTH) + .addHint(new ValueHint("Creature cards in your graveyard", new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE))) ); // {B}{G}, Sacrifice another creature: You gain 1 life and draw a card. diff --git a/Mage.Sets/src/mage/cards/j/JayaBallardTaskMage.java b/Mage.Sets/src/mage/cards/j/JayaBallardTaskMage.java index f76e0364491..9b492d456ef 100644 --- a/Mage.Sets/src/mage/cards/j/JayaBallardTaskMage.java +++ b/Mage.Sets/src/mage/cards/j/JayaBallardTaskMage.java @@ -1,7 +1,6 @@ package mage.cards.j; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; @@ -25,20 +24,21 @@ import mage.target.TargetPermanent; import mage.target.common.TargetAnyTarget; import mage.watchers.common.DamagedByWatcher; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class JayaBallardTaskMage extends CardImpl { private static final FilterPermanent filter = new FilterPermanent("blue permanent"); - + static { filter.add(new ColorPredicate(ObjectColor.BLUE)); } - + public JayaBallardTaskMage(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SPELLSHAPER); @@ -50,23 +50,23 @@ public final class JayaBallardTaskMage extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl<>("{R}")); ability.addTarget(new TargetPermanent(filter)); ability.addCost(new TapSourceCost()); - ability.addCost(new DiscardCardCost()); + ability.addCost(new DiscardCardCost()); this.addAbility(ability); - + // {1}{R}, {tap}, Discard a card: Jaya Ballard, Task Mage deals 3 damage to any target. A creature dealt damage this way can't be regenerated this turn. ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(3), new ManaCostsImpl<>("{1}{R}")); ability.addTarget(new TargetAnyTarget()); ability.addCost(new TapSourceCost()); - ability.addCost(new DiscardCardCost()); + ability.addCost(new DiscardCardCost()); ability.addEffect(new CantRegenerateEffect()); this.addAbility(ability, new DamagedByWatcher(false)); - + // {5}{R}{R}, {tap}, Discard a card: Jaya Ballard deals 6 damage to each creature and each player. ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageEverythingEffect(6), new ManaCostsImpl<>("{5}{R}{R}")); ability.addCost(new TapSourceCost()); - ability.addCost(new DiscardCardCost()); + ability.addCost(new DiscardCardCost()); this.addAbility(ability); - + } private JayaBallardTaskMage(final JayaBallardTaskMage card) { @@ -96,19 +96,13 @@ class CantRegenerateEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - return true; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.REGENERATE; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.REGENERATE) { - DamagedByWatcher watcher = game.getState().getWatcher(DamagedByWatcher.class, source.getSourceId()); - if (watcher != null) { - return watcher.wasDamaged(event.getTargetId(), game); - } - } - return false; + DamagedByWatcher watcher = game.getState().getWatcher(DamagedByWatcher.class, source.getSourceId()); + return watcher != null && watcher.wasDamaged(event.getTargetId(), game); } - } diff --git a/Mage.Sets/src/mage/cards/j/JettingGlasskite.java b/Mage.Sets/src/mage/cards/j/JettingGlasskite.java index 419194b95ed..ab68e05e1fe 100644 --- a/Mage.Sets/src/mage/cards/j/JettingGlasskite.java +++ b/Mage.Sets/src/mage/cards/j/JettingGlasskite.java @@ -1,24 +1,21 @@ - package mage.cards.j; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.keyword.FlyingAbility; 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.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.TargetStackObject; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * - * @author LevelX2 + * @author LevelX2, xenohedron */ public final class JettingGlasskite extends CardImpl { @@ -34,7 +31,10 @@ public final class JettingGlasskite extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Jetting Glasskite becomes the target of a spell or ability for the first time each turn, counter that spell or ability. - this.addAbility(new JettingGlasskiteAbility()); + this.addAbility(new BecomesTargetSourceFirstTimeTriggeredAbility( + new CounterTargetEffect().setText("counter that spell or ability"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.SPELL, false + )); } @@ -47,46 +47,3 @@ public final class JettingGlasskite extends CardImpl { return new JettingGlasskite(this); } } - -class JettingGlasskiteAbility extends TriggeredAbilityImpl { - - protected int turnUsed; - - public JettingGlasskiteAbility() { - super(Zone.BATTLEFIELD, new CounterTargetEffect(), false); - } - - private JettingGlasskiteAbility(final JettingGlasskiteAbility ability) { - super(ability); - turnUsed = ability.turnUsed; - } - - @Override - public JettingGlasskiteAbility copy() { - return new JettingGlasskiteAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getTurnNum() > turnUsed) { - this.getTargets().clear(); - TargetStackObject target = new TargetStackObject(); - target.add(event.getSourceId(), game); - this.addTarget(target); - turnUsed = game.getTurnNum(); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability for the first time each turn, counter that spell or ability."; - } - -} diff --git a/Mage.Sets/src/mage/cards/j/JumpTrooper.java b/Mage.Sets/src/mage/cards/j/JumpTrooper.java index c480508d453..becfa48239a 100644 --- a/Mage.Sets/src/mage/cards/j/JumpTrooper.java +++ b/Mage.Sets/src/mage/cards/j/JumpTrooper.java @@ -1,23 +1,18 @@ - package mage.cards.j; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterUnlessPaysEffect; 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.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreatureCard; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.TargetStackObject; + +import java.util.UUID; /** * @@ -25,7 +20,7 @@ import mage.target.TargetStackObject; */ public final class JumpTrooper extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("Trooper creatures"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a Trooper creature you control"); static { filter.add(SubType.TROOPER.getPredicate()); @@ -38,8 +33,10 @@ public final class JumpTrooper extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - // Whenever a Trooper creature you control becomes the target of a spell or ability an opponent controls, counter that spell or abitlity unless its controller pays {2}. - this.addAbility(new JumpTrooperTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(2)))); + // Whenever a Trooper creature you control becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {2}. + this.addAbility(new BecomesTargetAnyTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(2)) + .setText("counter that spell or ability unless its controller pays {2}"), + filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); } @@ -52,50 +49,3 @@ public final class JumpTrooper extends CardImpl { return new JumpTrooper(this); } } - -class JumpTrooperTriggeredAbility extends TriggeredAbilityImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Trooper creature you control"); - - static { - filter.add(SubType.TROOPER.getPredicate()); - } - - public JumpTrooperTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private JumpTrooperTriggeredAbility(final JumpTrooperTriggeredAbility ability) { - super(ability); - } - - @Override - public JumpTrooperTriggeredAbility copy() { - return new JumpTrooperTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature != null && filter.match(creature, getControllerId(), this, game)) { - this.getTargets().clear(); - TargetStackObject target = new TargetStackObject(); - target.add(event.getSourceId(), game); - this.addTarget(target); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a Trooper creature you control becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {2}."; - } -} diff --git a/Mage.Sets/src/mage/cards/k/KarplusanStrider.java b/Mage.Sets/src/mage/cards/k/KarplusanStrider.java index 57a5c7bc73c..b4548e168be 100644 --- a/Mage.Sets/src/mage/cards/k/KarplusanStrider.java +++ b/Mage.Sets/src/mage/cards/k/KarplusanStrider.java @@ -60,11 +60,6 @@ class KarplusanStriderEffect extends ContinuousRuleModifyingEffectImpl { return new KarplusanStriderEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/k/KatabaticWinds.java b/Mage.Sets/src/mage/cards/k/KatabaticWinds.java index 7aa58eeeab7..ac9e97bb0b1 100644 --- a/Mage.Sets/src/mage/cards/k/KatabaticWinds.java +++ b/Mage.Sets/src/mage/cards/k/KatabaticWinds.java @@ -104,11 +104,6 @@ class KatabaticWindsRuleModifyingEffect extends ContinuousRuleModifyingEffectImp return new KatabaticWindsRuleModifyingEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; diff --git a/Mage.Sets/src/mage/cards/k/KatildaDawnhartPrime.java b/Mage.Sets/src/mage/cards/k/KatildaDawnhartPrime.java index cf59dd20386..8e551fd7e7c 100644 --- a/Mage.Sets/src/mage/cards/k/KatildaDawnhartPrime.java +++ b/Mage.Sets/src/mage/cards/k/KatildaDawnhartPrime.java @@ -174,7 +174,7 @@ class KatildaDawnhartPrimeManaEffect extends ManaEffect { if (choice.getChoices().size() == 1) { choice.setChoice(choice.getChoices().iterator().next()); } else { - controller.choose(outcome, choice, game); + controller.choose(Outcome.PutManaInPool, choice, game); } if (choice.getChoice() == null) { return new Mana(); diff --git a/Mage.Sets/src/mage/cards/k/KayaIntangibleSlayer.java b/Mage.Sets/src/mage/cards/k/KayaIntangibleSlayer.java index e4761c7513f..2265ad9e61f 100644 --- a/Mage.Sets/src/mage/cards/k/KayaIntangibleSlayer.java +++ b/Mage.Sets/src/mage/cards/k/KayaIntangibleSlayer.java @@ -134,7 +134,7 @@ class KayaIntangibleSlayerExileEffect extends OneShotEffect { ); effect.setSavedPermanent(permanent); effect.setOnlyColor(ObjectColor.WHITE); - effect.setAdditionalSubType(SubType.SPIRIT); + effect.withAdditionalSubType(SubType.SPIRIT); effect.apply(game, source); return true; } diff --git a/Mage.Sets/src/mage/cards/k/KeenEaredSentry.java b/Mage.Sets/src/mage/cards/k/KeenEaredSentry.java index bb150d24609..a2025f4ff5b 100644 --- a/Mage.Sets/src/mage/cards/k/KeenEaredSentry.java +++ b/Mage.Sets/src/mage/cards/k/KeenEaredSentry.java @@ -83,11 +83,6 @@ class KeenEaredSentryEffect extends ContinuousRuleModifyingEffectImpl { return game.getOpponents(source.getControllerId()).contains(event.getTargetId()) && KeenEaredSentryWatcher.checkPlayer(event.getTargetId(), game); } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } } class KeenEaredSentryWatcher extends Watcher { diff --git a/Mage.Sets/src/mage/cards/k/KillSwitch.java b/Mage.Sets/src/mage/cards/k/KillSwitch.java index 34d31e5c511..4bd37431529 100644 --- a/Mage.Sets/src/mage/cards/k/KillSwitch.java +++ b/Mage.Sets/src/mage/cards/k/KillSwitch.java @@ -97,11 +97,6 @@ class KillSwitchUntapEffect extends ContinuousRuleModifyingEffectImpl { return new KillSwitchUntapEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.UNTAP; diff --git a/Mage.Sets/src/mage/cards/k/KingOfTheOathbreakers.java b/Mage.Sets/src/mage/cards/k/KingOfTheOathbreakers.java index 1d6f0e712b7..c9187f04f7f 100644 --- a/Mage.Sets/src/mage/cards/k/KingOfTheOathbreakers.java +++ b/Mage.Sets/src/mage/cards/k/KingOfTheOathbreakers.java @@ -1,7 +1,7 @@ package mage.cards.k; import mage.MageInt; -import mage.abilities.common.BecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.common.PhaseInTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.PhaseOutTargetEffect; @@ -43,7 +43,7 @@ public final class KingOfTheOathbreakers extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever King of the Oathbreakers or another Spirit you control becomes the target of a spell, it phases out. - this.addAbility(new BecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetAnyTriggeredAbility( new PhaseOutTargetEffect("it"), filter, StaticFilters.FILTER_SPELL_A )); diff --git a/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java b/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java index ad727125a6b..09faf71ee3b 100644 --- a/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java +++ b/Mage.Sets/src/mage/cards/k/KiraGreatGlassSpinner.java @@ -1,10 +1,9 @@ package mage.cards.k; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.FlyingAbility; @@ -12,11 +11,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher; + +import java.util.UUID; /** * @@ -36,12 +32,12 @@ public final class KiraGreatGlassSpinner extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Creatures you control have "Whenever this creature becomes the target of a spell or ability for the first time each turn, counter that spell or ability." - Effect effect = new CounterTargetEffect(); - effect.setText("counter that spell or ability"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityControlledEffect(new KiraGreatGlassSpinnerAbility(effect), Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURES)), - new NumberOfTimesPermanentTargetedATurnWatcher()); + TriggeredAbility gainedAbility = new BecomesTargetSourceFirstTimeTriggeredAbility( + new CounterTargetEffect().setText("counter that spell or ability"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.SPELL, false + ).setTriggerPhrase("Whenever this creature becomes the target of a spell or ability for the first time each turn, "); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + gainedAbility, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES))); } @@ -54,47 +50,3 @@ public final class KiraGreatGlassSpinner extends CardImpl { return new KiraGreatGlassSpinner(this); } } - -class KiraGreatGlassSpinnerAbility extends TriggeredAbilityImpl { - - public KiraGreatGlassSpinnerAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect, false); - } - - private KiraGreatGlassSpinnerAbility(final KiraGreatGlassSpinnerAbility ability) { - super(ability); - } - - @Override - public KiraGreatGlassSpinnerAbility copy() { - return new KiraGreatGlassSpinnerAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId())) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isCreature(game)) { - NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); - if (watcher != null && watcher.notMoreThanOnceTargetedThisTurn(permanent, game)) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId(), game)); - } - return true; - } - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever this creature becomes the target of a spell or ability for the first time each turn, counter that spell or ability."; - } - -} diff --git a/Mage.Sets/src/mage/cards/k/KjeldoranGuard.java b/Mage.Sets/src/mage/cards/k/KjeldoranGuard.java new file mode 100644 index 00000000000..ece65248115 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KjeldoranGuard.java @@ -0,0 +1,155 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.InvertCondition; +import mage.abilities.condition.common.DefendingPlayerControlsNoSourceCondition; +import mage.abilities.condition.common.IsPhaseCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterLandPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author Alex-Vasile, Susucr + */ +public final class KjeldoranGuard extends CardImpl { + + private static final FilterLandPermanent filter = new FilterLandPermanent(); + + static { + filter.add(SuperType.SNOW.getPredicate()); + } + + public KjeldoranGuard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {T}: Target creature gets +1/+1 until end of turn. When that creature leaves the battlefield this turn, sacrifice Kjeldoran Guard. Activate only during combat and only if defending player controls no snow lands. + CompoundCondition condition = new CompoundCondition( + new IsPhaseCondition(TurnPhase.COMBAT, false), // Only during combat + new InvertCondition(new DefendingPlayerControlsNoSourceCondition(filter)) // Only if defending player controls no snow land + ); + + Ability ability = new ConditionalActivatedAbility( + Zone.BATTLEFIELD, + new KjeldoranGuardEffect(), + new TapSourceCost(), + condition, + "{T}: Target creature gets +1/+1 until end of turn. " + + "When that creature leaves the battlefield this turn, sacrifice {this}. " + + "Activate only during combat and only if defending player controls no snow lands." + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private KjeldoranGuard(final KjeldoranGuard card) { + super(card); + } + + @Override + public KjeldoranGuard copy() { + return new KjeldoranGuard(this); + } +} + +class KjeldoranGuardEffect extends OneShotEffect { + + KjeldoranGuardEffect() { + super(Outcome.BoostCreature); + staticText = "Target creature gets +1/+1 until end of turn. " + + "When that creature leaves the battlefield this turn, sacrifice {this}."; + } + + private KjeldoranGuardEffect(KjeldoranGuardEffect effect) { + super(effect); + } + + @Override + public KjeldoranGuardEffect copy() { + return new KjeldoranGuardEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent targetPermanent = game.getPermanent(source.getFirstTarget()); + if (targetPermanent == null) { + return false; + } + + // Target creature gets +1/+1 until end of turn. + BoostTargetEffect buffEffect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); + buffEffect.setTargetPointer(new FixedTarget(targetPermanent, game)); + game.addEffect(buffEffect, source); + + // When that creature leaves the battlefield this turn, sacrifice Kjeldoran Guard. + DelayedTriggeredAbility delayed = new KjeldoranGuardDelayedTriggeredAbility( + new MageObjectReference(targetPermanent, game) + ); + + // Locking in the Kjeldoran Guard (and its zcc), to be sacrificed later. + Permanent guard = source.getSourcePermanentIfItStillExists(game); + if(guard != null) { + delayed.getEffects().setTargetPointer(new FixedTarget(guard, game)); + } + game.addDelayedTriggeredAbility(delayed, source); + + return true; + } +} + +class KjeldoranGuardDelayedTriggeredAbility extends DelayedTriggeredAbility { + + private final MageObjectReference mor; + + KjeldoranGuardDelayedTriggeredAbility(MageObjectReference mor) { + super(new SacrificeTargetEffect(), Duration.EndOfTurn, true); + this.mor = mor; + } + + private KjeldoranGuardDelayedTriggeredAbility(final KjeldoranGuardDelayedTriggeredAbility ability) { + super(ability); + this.mor = ability.mor; + } + + @Override + public KjeldoranGuardDelayedTriggeredAbility copy() { + return new KjeldoranGuardDelayedTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanentLeaving = game.getPermanentOrLKIBattlefield(event.getTargetId()); + return permanentLeaving != null && mor.equals(new MageObjectReference(permanentLeaving, game)); + } + + @Override + public String getRule() { + return "When that creature leaves the battlefield, Sacrifice {this}"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/l/LabyrinthGuardian.java b/Mage.Sets/src/mage/cards/l/LabyrinthGuardian.java index 805643e8e95..faf2adeb0fa 100644 --- a/Mage.Sets/src/mage/cards/l/LabyrinthGuardian.java +++ b/Mage.Sets/src/mage/cards/l/LabyrinthGuardian.java @@ -1,10 +1,7 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.EmbalmAbility; @@ -12,16 +9,12 @@ 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.events.GameEvent.EventType; -import mage.game.stack.Spell; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author fireshoes + * @author xenohedron */ public final class LabyrinthGuardian extends CardImpl { @@ -34,7 +27,7 @@ public final class LabyrinthGuardian extends CardImpl { this.toughness = new MageInt(3); // When Labyrinth Guardian becomes the target of a spell, sacrifice it. - this.addAbility(new LabyrinthGuardianTriggeredAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A)); // Embalm {3}{U} this.addAbility(new EmbalmAbility(new ManaCostsImpl<>("{3}{U}"), this)); @@ -50,40 +43,3 @@ public final class LabyrinthGuardian extends CardImpl { return new LabyrinthGuardian(this); } } - -class LabyrinthGuardianTriggeredAbility extends TriggeredAbilityImpl { - - public LabyrinthGuardianTriggeredAbility() { - super(Zone.BATTLEFIELD, new SacrificeSourceEffect(), false); - } - - private LabyrinthGuardianTriggeredAbility(final LabyrinthGuardianTriggeredAbility ability) { - super(ability); - } - - @Override - public LabyrinthGuardianTriggeredAbility copy() { - return new LabyrinthGuardianTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - MageObject eventSourceObject = game.getObject(event.getSourceId()); - if (event.getTargetId().equals(this.getSourceId()) && eventSourceObject instanceof Spell) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - return false; - } - - @Override - public String getRule() { - return "When {this} becomes the target of a spell, sacrifice it."; - } - -} diff --git a/Mage.Sets/src/mage/cards/l/LavaRunner.java b/Mage.Sets/src/mage/cards/l/LavaRunner.java index d2e9aa10a49..ee1ae0b4812 100644 --- a/Mage.Sets/src/mage/cards/l/LavaRunner.java +++ b/Mage.Sets/src/mage/cards/l/LavaRunner.java @@ -1,26 +1,20 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeEffect; import mage.abilities.keyword.HasteAbility; 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.common.FilterLandPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author LoneFox - + * @author xenohedron */ public final class LavaRunner extends CardImpl { @@ -32,8 +26,11 @@ public final class LavaRunner extends CardImpl { // Haste this.addAbility(HasteAbility.getInstance()); + // Whenever Lava Runner becomes the target of a spell or ability, that spell or ability's controller sacrifices a land. - this.addAbility(new LavaRunnerAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility( + new SacrificeEffect(StaticFilters.FILTER_LAND_A, 1, "that spell or ability's controller"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.PLAYER, false)); } private LavaRunner(final LavaRunner card) { @@ -45,38 +42,3 @@ public final class LavaRunner extends CardImpl { return new LavaRunner(this); } } - -class LavaRunnerAbility extends TriggeredAbilityImpl { - - public LavaRunnerAbility() { - super(Zone.BATTLEFIELD, new SacrificeEffect(new FilterLandPermanent(), 1, ""), false); - } - - private LavaRunnerAbility(final LavaRunnerAbility ability) { - super(ability); - } - - @Override - public LavaRunnerAbility copy() { - return new LavaRunnerAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if(event.getTargetId().equals(this.getSourceId())) { - getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability, that spell or ability's controller sacrifices a land."; - } -} diff --git a/Mage.Sets/src/mage/cards/l/LavabellySliver.java b/Mage.Sets/src/mage/cards/l/LavabellySliver.java index 303049facca..891c3560046 100644 --- a/Mage.Sets/src/mage/cards/l/LavabellySliver.java +++ b/Mage.Sets/src/mage/cards/l/LavabellySliver.java @@ -31,8 +31,7 @@ public final class LavabellySliver extends CardImpl { // Sliver creatures you control have "When this creature enters the battlefield, it deals 1 damage to target player or planeswalker and you gain 1 life." Ability ability = new EntersBattlefieldTriggeredAbility( - new DamageTargetEffect(1, "it"), - false, true + new DamageTargetEffect(1, "it"), false ).setTriggerPhrase("When this creature enters the battlefield, "); ability.addEffect(new GainLifeEffect(1).concatBy("and")); ability.addTarget(new TargetPlayerOrPlaneswalker()); diff --git a/Mage.Sets/src/mage/cards/l/LazierGoblin.java b/Mage.Sets/src/mage/cards/l/LazierGoblin.java new file mode 100644 index 00000000000..121235d0a23 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LazierGoblin.java @@ -0,0 +1,182 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.SpecialAction; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.common.ConditionPermanentHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class LazierGoblin extends CardImpl { + + /** + * Key to store that a MOR permanent has been motivated by a player. + */ + static final String keyMotivation(UUID playerId, MageObjectReference mor) { + return "LazierGoblinMotivated|" + playerId.toString() + "|" + mor.getSourceId().toString() + "|" + mor.getZoneChangeCounter(); + } + + public LazierGoblin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); + + this.subtype.add(SubType.GOBLIN); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Motivate {3}{R} (This creature can't attack or block unless you have paid its motivate cost once. Motivate only as a sorcery.) + this.addAbility(new LazierGoblinSpecialAction()); + this.addAbility(new SimpleStaticAbility(new LazierGoblinRestrictionEffect()) + .addHint(new ConditionPermanentHint(LazierGoblinMotivatedCondition.instance))); + + // When Lazier Goblin enters the battlefield, it deals 2 damage to any target. + Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(2, "it")); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + } + + private LazierGoblin(final LazierGoblin card) { + super(card); + } + + @Override + public LazierGoblin copy() { + return new LazierGoblin(this); + } +} + +class LazierGoblinSpecialAction extends SpecialAction { + + // Ruling (2019-11-12): + // Paying a creature's motivate cost is a special action that its controller + // may take any time they have priority during their main phase with no spells + // or abilities on the stack. + + LazierGoblinSpecialAction() { + super(Zone.BATTLEFIELD); + this.setTiming(TimingRule.SORCERY); + this.addCost(new ManaCostsImpl<>("{3}{R}")); + this.addEffect(new LazierGoblinMotivateEffect().setText("Motivate {3}{R}")); // text is for game log to make sense. + } + + private LazierGoblinSpecialAction(final LazierGoblinSpecialAction ability) { + super(ability); + } + + @Override + public LazierGoblinSpecialAction copy() { + return new LazierGoblinSpecialAction(this); + } + + @Override + public String getRule() { + return "Motivate {3}{R} (This creature can't attack or block unless you have paid its motivate cost once. Motivate only as a sorcery.)"; + } +} + +class LazierGoblinMotivateEffect extends OneShotEffect { + LazierGoblinMotivateEffect() { + super(Outcome.Benefit); + } + + private LazierGoblinMotivateEffect(final LazierGoblinMotivateEffect effect) { + super(effect); + } + + @Override + public LazierGoblinMotivateEffect copy() { + return new LazierGoblinMotivateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (permanent == null) { + return false; + } + game.getState().setValue( + LazierGoblin.keyMotivation( + permanent.getControllerId(), + new MageObjectReference(permanent, game) + ), + true + ); + return true; + } +} + +class LazierGoblinRestrictionEffect extends RestrictionEffect { + + public LazierGoblinRestrictionEffect() { + super(Duration.WhileOnBattlefield); + staticText = ""; // no text. + } + + private LazierGoblinRestrictionEffect(final LazierGoblinRestrictionEffect effect) { + super(effect); + } + + @Override + public LazierGoblinRestrictionEffect copy() { + return new LazierGoblinRestrictionEffect(this); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return permanent != null + && permanent.getId().equals(source.getSourceId()) + && null == game.getState().getValue( + LazierGoblin.keyMotivation( + permanent.getControllerId(), + new MageObjectReference(permanent, game) + ) + ); + } + + @Override + public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game, boolean canUseChooseDialogs) { + return false; + } + + @Override + public boolean canAttack(Game game, boolean canUseChooseDialogs) { + return false; + } +} + +enum LazierGoblinMotivatedCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + return permanent != null + && null != game.getState().getValue( + LazierGoblin.keyMotivation( + permanent.getControllerId(), + new MageObjectReference(permanent, game) + ) + ); + } + + @Override + public String toString() { + return "motivated"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/l/LegolassQuickReflexes.java b/Mage.Sets/src/mage/cards/l/LegolassQuickReflexes.java new file mode 100644 index 00000000000..ebd6ceea46e --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LegolassQuickReflexes.java @@ -0,0 +1,65 @@ +package mage.cards.l; + +import mage.abilities.TriggeredAbility; +import mage.abilities.common.BecomesTappedSourceTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.SplitSecondAbility; +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 Susucr + */ +public final class LegolassQuickReflexes extends CardImpl { + + private static final DynamicValue xValue = new SourcePermanentPowerCount(); + + public LegolassQuickReflexes(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); + + // Split second + this.addAbility(new SplitSecondAbility()); + + // Untap target creature. Until end of turn, it gains hexproof, reach, and "Whenever this creature becomes tapped, it deals damage equal to its power to up to one target creature." + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new UntapTargetEffect()); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + HexproofAbility.getInstance(), Duration.EndOfTurn + ).setText("Until end of turn, it gains hexproof")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + ReachAbility.getInstance(), Duration.EndOfTurn + ).setText(", reach")); + + TriggeredAbility trigger = new BecomesTappedSourceTriggeredAbility( + new DamageTargetEffect(xValue) + .setText("it deals damage equal to its power to up to one target creature"), + false + ); + trigger.addTarget(new TargetCreaturePermanent(0, 1)); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + trigger, Duration.EndOfTurn + ).setText(", and \"Whenever this creature becomes tapped, " + + "it deals damage equal to its power to up to one target creature.\"")); + } + + private LegolassQuickReflexes(final LegolassQuickReflexes card) { + super(card); + } + + @Override + public LegolassQuickReflexes copy() { + return new LegolassQuickReflexes(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LeovoldEmissaryOfTrest.java b/Mage.Sets/src/mage/cards/l/LeovoldEmissaryOfTrest.java index a872ffee356..1fbbee4ff1f 100644 --- a/Mage.Sets/src/mage/cards/l/LeovoldEmissaryOfTrest.java +++ b/Mage.Sets/src/mage/cards/l/LeovoldEmissaryOfTrest.java @@ -1,15 +1,15 @@ - package mage.cards.l; import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.common.TargetOfOpponentsSpellOrAbilityTriggeredAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; @@ -35,7 +35,8 @@ public final class LeovoldEmissaryOfTrest extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LeovoldEmissaryOfTrestEffect()), new CardsAmountDrawnThisTurnWatcher()); // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. - this.addAbility(new TargetOfOpponentsSpellOrAbilityTriggeredAbility(new DrawCardSourceControllerEffect(1), true, false)); + this.addAbility(new BecomesTargetControllerTriggeredAbility(new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true)); } private LeovoldEmissaryOfTrest(final LeovoldEmissaryOfTrest card) { @@ -69,11 +70,6 @@ class LeovoldEmissaryOfTrestEffect extends ContinuousRuleModifyingEffectImpl { return event.getType() == GameEvent.EventType.DRAW_CARD; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean applies(GameEvent event, Ability source, Game game) { CardsAmountDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsAmountDrawnThisTurnWatcher.class); @@ -81,4 +77,4 @@ class LeovoldEmissaryOfTrestEffect extends ContinuousRuleModifyingEffectImpl { return watcher != null && controller != null && watcher.getAmountCardsDrawn(event.getPlayerId()) >= 1 && game.isOpponent(controller, event.getPlayerId()); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/l/LeylineOfLifeforce.java b/Mage.Sets/src/mage/cards/l/LeylineOfLifeforce.java index 7a1d1a1d6ff..394fdc95a9d 100644 --- a/Mage.Sets/src/mage/cards/l/LeylineOfLifeforce.java +++ b/Mage.Sets/src/mage/cards/l/LeylineOfLifeforce.java @@ -59,11 +59,6 @@ class LeylineOfLifeforceEffect extends ContinuousRuleModifyingEffectImpl { return new LeylineOfLifeforceEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.COUNTER; diff --git a/Mage.Sets/src/mage/cards/l/LichsMastery.java b/Mage.Sets/src/mage/cards/l/LichsMastery.java index 53dd428688e..18aaa2a26e3 100644 --- a/Mage.Sets/src/mage/cards/l/LichsMastery.java +++ b/Mage.Sets/src/mage/cards/l/LichsMastery.java @@ -89,13 +89,13 @@ class LichsMasteryCantLoseEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - return true; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LOSES; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - return event.getType() == GameEvent.EventType.LOSES && event.getPlayerId().equals(source.getControllerId()); + return event.getPlayerId().equals(source.getControllerId()); } } diff --git a/Mage.Sets/src/mage/cards/l/LierDiscipleOfTheDrowned.java b/Mage.Sets/src/mage/cards/l/LierDiscipleOfTheDrowned.java index 31df8e4d977..0d2d604c70e 100644 --- a/Mage.Sets/src/mage/cards/l/LierDiscipleOfTheDrowned.java +++ b/Mage.Sets/src/mage/cards/l/LierDiscipleOfTheDrowned.java @@ -64,11 +64,6 @@ class LierDiscipleOfTheDrownedCounteredEffect extends ContinuousRuleModifyingEff return new LierDiscipleOfTheDrownedCounteredEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.COUNTER; diff --git a/Mage.Sets/src/mage/cards/l/LimitedResources.java b/Mage.Sets/src/mage/cards/l/LimitedResources.java index 021d130b24b..d4b00ce2a55 100644 --- a/Mage.Sets/src/mage/cards/l/LimitedResources.java +++ b/Mage.Sets/src/mage/cards/l/LimitedResources.java @@ -106,11 +106,6 @@ class CantPlayLandEffect extends ContinuousRuleModifyingEffectImpl { return new CantPlayLandEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.PLAY_LAND; diff --git a/Mage.Sets/src/mage/cards/l/LivewireLash.java b/Mage.Sets/src/mage/cards/l/LivewireLash.java index b432e3004df..a2ef4037517 100644 --- a/Mage.Sets/src/mage/cards/l/LivewireLash.java +++ b/Mage.Sets/src/mage/cards/l/LivewireLash.java @@ -1,7 +1,7 @@ package mage.cards.l; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DamageTargetEffect; @@ -30,7 +30,7 @@ public final class LivewireLash extends CardImpl { // Equipped creature gets +2/+0 and has "Whenever this creature becomes the target of a spell, this creature deals 2 damage to any target." Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 0)); - Ability ability2 = new SourceBecomesTargetTriggeredAbility( + Ability ability2 = new BecomesTargetSourceTriggeredAbility( new DamageTargetEffect(2, "it"), StaticFilters.FILTER_SPELL_A ).setTriggerPhrase("Whenever this creature becomes the target of a spell, "); ability2.addTarget(new TargetAnyTarget()); diff --git a/Mage.Sets/src/mage/cards/l/LivingBreakthrough.java b/Mage.Sets/src/mage/cards/l/LivingBreakthrough.java index 5d411f78c99..861156384e3 100644 --- a/Mage.Sets/src/mage/cards/l/LivingBreakthrough.java +++ b/Mage.Sets/src/mage/cards/l/LivingBreakthrough.java @@ -100,9 +100,4 @@ class LivingBreakthroughEffect extends ContinuousRuleModifyingEffectImpl { Spell spell = game.getStack().getSpell(event.getTargetId()); return spell != null && spell.getManaValue() == this.manaValue; } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } } diff --git a/Mage.Sets/src/mage/cards/l/LlawanCephalidEmpress.java b/Mage.Sets/src/mage/cards/l/LlawanCephalidEmpress.java index a2cf6982fce..523e9185695 100644 --- a/Mage.Sets/src/mage/cards/l/LlawanCephalidEmpress.java +++ b/Mage.Sets/src/mage/cards/l/LlawanCephalidEmpress.java @@ -84,11 +84,6 @@ class LlawanCephalidRuleModifyingEffect extends ContinuousRuleModifyingEffectImp return new LlawanCephalidRuleModifyingEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/l/LostInThought.java b/Mage.Sets/src/mage/cards/l/LostInThought.java index cc744edb9fa..1ea81040989 100644 --- a/Mage.Sets/src/mage/cards/l/LostInThought.java +++ b/Mage.Sets/src/mage/cards/l/LostInThought.java @@ -117,11 +117,6 @@ class LostInThoughtCantActivateAbilitiesEffect extends ContinuousRuleModifyingEf return new LostInThoughtCantActivateAbilitiesEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; diff --git a/Mage.Sets/src/mage/cards/l/LureOfPrey.java b/Mage.Sets/src/mage/cards/l/LureOfPrey.java index 90cf1b4144c..61de86d7be7 100644 --- a/Mage.Sets/src/mage/cards/l/LureOfPrey.java +++ b/Mage.Sets/src/mage/cards/l/LureOfPrey.java @@ -82,11 +82,6 @@ class LureOfPreyRestrictionEffect extends ContinuousRuleModifyingEffectImpl { return false; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public LureOfPreyRestrictionEffect copy() { return new LureOfPreyRestrictionEffect(this); diff --git a/Mage.Sets/src/mage/cards/l/LurkingJackals.java b/Mage.Sets/src/mage/cards/l/LurkingJackals.java index ff7c39df028..d21f0b5866c 100644 --- a/Mage.Sets/src/mage/cards/l/LurkingJackals.java +++ b/Mage.Sets/src/mage/cards/l/LurkingJackals.java @@ -42,6 +42,7 @@ class LurkingJackalsStateTriggeredAbility extends StateTriggeredAbility { public LurkingJackalsStateTriggeredAbility() { super(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new LurkingJackalsToken(), null, Duration.Custom)); setTriggerPhrase("When an opponent has 10 or less life, if {this} is an enchantment, "); + this.replaceRuleText = true; } private LurkingJackalsStateTriggeredAbility(final LurkingJackalsStateTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java b/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java index 7d69a47fb48..d152ee8a24e 100644 --- a/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java +++ b/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java @@ -2,7 +2,7 @@ package mage.cards.m; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; @@ -116,7 +116,7 @@ class MakeshiftMannequinGainAbilityEffect extends ContinuousEffectImpl { Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); if (permanent != null) { permanent.addAbility( - new SourceBecomesTargetTriggeredAbility( + new BecomesTargetSourceTriggeredAbility( new SacrificeSourceEffect()), source.getSourceId(), game); return true; diff --git a/Mage.Sets/src/mage/cards/m/ManaMaze.java b/Mage.Sets/src/mage/cards/m/ManaMaze.java index f98310f1e09..7ce36a4fec6 100644 --- a/Mage.Sets/src/mage/cards/m/ManaMaze.java +++ b/Mage.Sets/src/mage/cards/m/ManaMaze.java @@ -72,11 +72,6 @@ class ManaMazeEffect extends ContinuousRuleModifyingEffectImpl { return false; } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public ManaMazeEffect copy() { return new ManaMazeEffect(this); diff --git a/Mage.Sets/src/mage/cards/m/MandateOfPeace.java b/Mage.Sets/src/mage/cards/m/MandateOfPeace.java index 973f73f3de6..bb234bf6228 100644 --- a/Mage.Sets/src/mage/cards/m/MandateOfPeace.java +++ b/Mage.Sets/src/mage/cards/m/MandateOfPeace.java @@ -65,11 +65,6 @@ class MandateOfPeaceOpponentsCantCastSpellsEffect extends ContinuousRuleModifyin return new MandateOfPeaceOpponentsCantCastSpellsEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/m/MaralenOfTheMornsong.java b/Mage.Sets/src/mage/cards/m/MaralenOfTheMornsong.java index eb0c341ac1a..6ede71a79c4 100644 --- a/Mage.Sets/src/mage/cards/m/MaralenOfTheMornsong.java +++ b/Mage.Sets/src/mage/cards/m/MaralenOfTheMornsong.java @@ -60,11 +60,6 @@ class MaralenOfTheMornsongEffect extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public MaralenOfTheMornsongEffect copy() { return new MaralenOfTheMornsongEffect(this); diff --git a/Mage.Sets/src/mage/cards/m/MarisiBreakerOfTheCoil.java b/Mage.Sets/src/mage/cards/m/MarisiBreakerOfTheCoil.java index a1b006a56ac..a1efe3883a1 100644 --- a/Mage.Sets/src/mage/cards/m/MarisiBreakerOfTheCoil.java +++ b/Mage.Sets/src/mage/cards/m/MarisiBreakerOfTheCoil.java @@ -67,11 +67,6 @@ class MarisiBreakerOfTheCoilSpellEffect extends ContinuousRuleModifyingEffectImp return new MarisiBreakerOfTheCoilSpellEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java index 8e1af2b218f..303eb9ed934 100644 --- a/Mage.Sets/src/mage/cards/m/MasterWarcraft.java +++ b/Mage.Sets/src/mage/cards/m/MasterWarcraft.java @@ -85,11 +85,6 @@ class MasterWarcraftChooseAttackersEffect extends ContinuousRuleModifyingEffectI return new MasterWarcraftChooseAttackersEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DECLARING_ATTACKERS; diff --git a/Mage.Sets/src/mage/cards/m/MeddlingMage.java b/Mage.Sets/src/mage/cards/m/MeddlingMage.java index 02e66b49421..2ae5c9f41e5 100644 --- a/Mage.Sets/src/mage/cards/m/MeddlingMage.java +++ b/Mage.Sets/src/mage/cards/m/MeddlingMage.java @@ -59,11 +59,6 @@ class MeddlingMageReplacementEffect extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public MeddlingMageReplacementEffect copy() { return new MeddlingMageReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/m/MegatronTyrant.java b/Mage.Sets/src/mage/cards/m/MegatronTyrant.java index 58a7d8c4c9e..9349b01c200 100644 --- a/Mage.Sets/src/mage/cards/m/MegatronTyrant.java +++ b/Mage.Sets/src/mage/cards/m/MegatronTyrant.java @@ -83,11 +83,6 @@ class MegatronTyrantCantCastSpellsEffect extends ContinuousRuleModifyingEffectIm return new MegatronTyrantCantCastSpellsEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/m/MemoryCrystal.java b/Mage.Sets/src/mage/cards/m/MemoryCrystal.java index f5cd50a1dcd..41b5677e707 100644 --- a/Mage.Sets/src/mage/cards/m/MemoryCrystal.java +++ b/Mage.Sets/src/mage/cards/m/MemoryCrystal.java @@ -14,9 +14,7 @@ import mage.constants.CardType; import mage.constants.CostModificationType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; -import mage.game.stack.Spell; import mage.util.CardUtil; /** @@ -29,7 +27,7 @@ public final class MemoryCrystal extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // Buyback costs cost {2} less. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MemoryCrystalSpellsCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(new MemoryCrystalSpellsCostReductionEffect())); } private MemoryCrystal(final MemoryCrystal card) { @@ -44,26 +42,23 @@ public final class MemoryCrystal extends CardImpl { class MemoryCrystalSpellsCostReductionEffect extends CostModificationEffectImpl { - public MemoryCrystalSpellsCostReductionEffect() { + MemoryCrystalSpellsCostReductionEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); this.staticText = "Buyback costs cost {2} less."; } - protected MemoryCrystalSpellsCostReductionEffect(final MemoryCrystalSpellsCostReductionEffect effect) { + private MemoryCrystalSpellsCostReductionEffect(final MemoryCrystalSpellsCostReductionEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { - Card card = game.getCard(abilityToModify.getSourceId()); if (card != null) { for (Ability ability : card.getAbilities(game)) { - if (ability instanceof BuybackAbility) { - if (ability.isActivated()) { - int amountToReduce = ((BuybackAbility) ability).reduceCost(2); - CardUtil.reduceCost(abilityToModify, amountToReduce); - } + if (ability instanceof BuybackAbility && ability.isActivated()) { + int amountToReduce = ((BuybackAbility) ability).reduceCost(2); + CardUtil.reduceCost(abilityToModify, amountToReduce); } } } @@ -73,14 +68,7 @@ class MemoryCrystalSpellsCostReductionEffect extends CostModificationEffectImpl @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify instanceof SpellAbility) { - if (abilityToModify.isControlledBy(source.getControllerId())) { - Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId()); - if (spell != null) { - if (BuybackCondition.instance.apply(game, abilityToModify)) { - return true; - } - } - } + return game.getSpell(abilityToModify.getId()) != null && BuybackCondition.instance.apply(game, abilityToModify); } return false; } diff --git a/Mage.Sets/src/mage/cards/m/MeteorCrater.java b/Mage.Sets/src/mage/cards/m/MeteorCrater.java index d1857ec141a..976830e7ba2 100644 --- a/Mage.Sets/src/mage/cards/m/MeteorCrater.java +++ b/Mage.Sets/src/mage/cards/m/MeteorCrater.java @@ -11,6 +11,7 @@ import mage.cards.CardSetInfo; import mage.choices.Choice; import mage.constants.CardType; import mage.constants.ManaType; +import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; @@ -72,12 +73,17 @@ class MeteorCraterEffect extends ManaEffect { if (types.isEmpty()) { return null; } + + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return null; + } + Choice choice = ManaType.getChoiceOfManaTypes(types, true); if (choice.getChoices().size() == 1) { choice.setChoice(choice.getChoices().iterator().next()); } else { - Player player = game.getPlayer(source.getControllerId()); - if (player == null || !player.choose(outcome, choice, game)) { + if (!player.choose(Outcome.PutManaInPool, choice, game)) { return null; } } diff --git a/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java b/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java index a5283caa4ce..8ea82fa23b2 100644 --- a/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java +++ b/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java @@ -3,7 +3,7 @@ package mage.cards.m; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.BecomesTargetOpponentAllTriggeredAbility; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -52,8 +52,8 @@ public final class MilaCraftyCompanion extends ModalDoubleFacedCard { 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 BecomesTargetOpponentAllTriggeredAbility( - new DrawCardSourceControllerEffect(1), true + this.getLeftHalfCard().addAbility(new BecomesTargetAnyTriggeredAbility(new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true )); // 2. diff --git a/Mage.Sets/src/mage/cards/m/MinasTirithGarrison.java b/Mage.Sets/src/mage/cards/m/MinasTirithGarrison.java new file mode 100644 index 00000000000..20c4ac70836 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MinasTirithGarrison.java @@ -0,0 +1,107 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class MinasTirithGarrison extends CardImpl { + + public MinasTirithGarrison(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(0); + this.toughness = new MageInt(5); + + // Minas Tirith Garrison's power is equal to the number of cards in your hand. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SetBasePowerSourceEffect(CardsInControllerHandCount.instance) + )); + + // Whenever Minas Tirith Garrison attacks, you may tap any number of untapped Humans you control. Draw a card for each Human tapped this way. + this.addAbility(new AttacksTriggeredAbility( + new MinasTirithGarrisonEffect(), true + )); + } + + private MinasTirithGarrison(final MinasTirithGarrison card) { + super(card); + } + + @Override + public MinasTirithGarrison copy() { + return new MinasTirithGarrison(this); + } +} + +// Based of Devout Invocation. +class MinasTirithGarrisonEffect extends OneShotEffect { + + static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.HUMAN, "untapped Humans you control"); + + static { + filter.add(TappedPredicate.UNTAPPED); + } + + public MinasTirithGarrisonEffect() { + super(Outcome.AIDontUseIt); + staticText = "tap any number of untapped Humans you control. Draw a card for each Human tapped this way"; + } + + private MinasTirithGarrisonEffect(final MinasTirithGarrisonEffect effect) { + super(effect); + } + + @Override + public MinasTirithGarrisonEffect copy() { + return new MinasTirithGarrisonEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + + TargetPermanent target = new TargetControlledPermanent(0, Integer.MAX_VALUE, filter, true); + controller.choose(outcome, target, source, game); + if (target.getTargets().isEmpty()) { + return false; + } + + int tappedAmount = 0; + for (UUID permanentId : target.getTargets()) { + Permanent permanent = game.getPermanent(permanentId); + if (permanent != null && permanent.tap(source, game)) { + tappedAmount++; + } + } + + return new DrawCardSourceControllerEffect(tappedAmount).apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MindlockOrb.java b/Mage.Sets/src/mage/cards/m/MindlockOrb.java index 1523e6e44b9..043593aeb5f 100644 --- a/Mage.Sets/src/mage/cards/m/MindlockOrb.java +++ b/Mage.Sets/src/mage/cards/m/MindlockOrb.java @@ -52,13 +52,13 @@ class MindlockRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - throw new UnsupportedOperationException("Not supported."); + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.SEARCH_LIBRARY; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - return event.getType() == GameEvent.EventType.SEARCH_LIBRARY; + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/m/MirkwoodChanneler.java b/Mage.Sets/src/mage/cards/m/MirkwoodChanneler.java new file mode 100644 index 00000000000..113721ad445 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MirkwoodChanneler.java @@ -0,0 +1,66 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +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.constants.TargetController; +import mage.filter.common.FilterControlledPermanent; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class MirkwoodChanneler extends CardImpl { + + private static final FilterControlledPermanent filter = + new FilterControlledPermanent(SubType.ELF, "Elf you control"); + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount( + new FilterControlledPermanent(SubType.FOREST, "Forests you control"), null + ); + private static final Hint hint = new ValueHint("Forests you control", xValue); + + public MirkwoodChanneler(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(3); + this.toughness = new MageInt(3); + + // At the beginning of combat on your turn, target Elf you control gains trample and gets +X/+X until end of turn, where X is the number of Forests you control. + Ability ability = new BeginningOfCombatTriggeredAbility( + new GainAbilityTargetEffect(TrampleAbility.getInstance()) + .setText("target Elf you control gains trample"), + TargetController.YOU, false + ); + ability.addEffect(new BoostTargetEffect(xValue, xValue) + .setText("and gets +X/+X until end of turn, where X is the number of Forests you control") + ); + ability.addTarget(new TargetControlledPermanent(filter)); + ability.addHint(hint); + this.addAbility(ability); + } + + private MirkwoodChanneler(final MirkwoodChanneler card) { + super(card); + } + + @Override + public MirkwoodChanneler copy() { + return new MirkwoodChanneler(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/Mirozel.java b/Mage.Sets/src/mage/cards/m/Mirozel.java index af1fd85e638..8eea6ec34e1 100644 --- a/Mage.Sets/src/mage/cards/m/Mirozel.java +++ b/Mage.Sets/src/mage/cards/m/Mirozel.java @@ -3,7 +3,7 @@ package mage.cards.m; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; @@ -28,7 +28,7 @@ public final class Mirozel extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Mirozel becomes the target of a spell or ability, return Mirozel to its owner's hand. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new ReturnToHandSourceEffect(true))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new ReturnToHandSourceEffect(true))); } private Mirozel(final Mirozel card) { diff --git a/Mage.Sets/src/mage/cards/m/MirrorMockery.java b/Mage.Sets/src/mage/cards/m/MirrorMockery.java index 4d2cad23b58..9db6f53a57f 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorMockery.java +++ b/Mage.Sets/src/mage/cards/m/MirrorMockery.java @@ -15,7 +15,6 @@ import mage.constants.AttachmentType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -71,12 +70,7 @@ class MirrorMockeryEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/m/MoltenDisaster.java b/Mage.Sets/src/mage/cards/m/MoltenDisaster.java index 27cadee434b..0ac4cd77c07 100644 --- a/Mage.Sets/src/mage/cards/m/MoltenDisaster.java +++ b/Mage.Sets/src/mage/cards/m/MoltenDisaster.java @@ -91,11 +91,6 @@ class MoltenDisasterSplitSecondEffect extends ContinuousRuleModifyingEffectImpl return false; } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public MoltenDisasterSplitSecondEffect copy() { return new MoltenDisasterSplitSecondEffect(this); diff --git a/Mage.Sets/src/mage/cards/m/Moonhold.java b/Mage.Sets/src/mage/cards/m/Moonhold.java index 10ee314921d..aa760e97591 100644 --- a/Mage.Sets/src/mage/cards/m/Moonhold.java +++ b/Mage.Sets/src/mage/cards/m/Moonhold.java @@ -70,11 +70,6 @@ class MoonholdEffect extends ContinuousRuleModifyingEffectImpl { return new MoonholdEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -113,11 +108,6 @@ class MoonholdEffect2 extends ContinuousRuleModifyingEffectImpl { return new MoonholdEffect2(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/m/Mossdog.java b/Mage.Sets/src/mage/cards/m/Mossdog.java index 677884fbd61..a4613140e75 100644 --- a/Mage.Sets/src/mage/cards/m/Mossdog.java +++ b/Mage.Sets/src/mage/cards/m/Mossdog.java @@ -1,23 +1,19 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; 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.GameEvent.EventType; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author fireshoes & L_J + * @author xenohedron */ public final class Mossdog extends CardImpl { @@ -29,7 +25,8 @@ public final class Mossdog extends CardImpl { this.toughness = new MageInt(1); // Whenever Mossdog becomes the target of a spell or ability an opponent controls, put a +1/+1 counter on Mossdog. - this.addAbility(new MossdogAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS)); } private Mossdog(final Mossdog card) { @@ -41,37 +38,3 @@ public final class Mossdog extends CardImpl { return new Mossdog(this); } } - -class MossdogAbility extends TriggeredAbilityImpl { - - public MossdogAbility() { - super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false); - } - - private MossdogAbility(final MossdogAbility ability) { - super(ability); - } - - @Override - public MossdogAbility copy() { - return new MossdogAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability an opponent controls, put a +1/+1 counter on {this}."; - } -} diff --git a/Mage.Sets/src/mage/cards/m/MyriadConstruct.java b/Mage.Sets/src/mage/cards/m/MyriadConstruct.java index 1de37fe7492..116e49c98fa 100644 --- a/Mage.Sets/src/mage/cards/m/MyriadConstruct.java +++ b/Mage.Sets/src/mage/cards/m/MyriadConstruct.java @@ -2,7 +2,7 @@ package mage.cards.m; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.KickedCondition; import mage.abilities.dynamicvalue.DynamicValue; @@ -60,7 +60,7 @@ public final class MyriadConstruct extends CardImpl { )); // When Myriad Construct becomes the target of a spell, sacrifice it and create a number of 1/1 colourless Construct artifact creature tokens equal to its power. - Ability ability = new SourceBecomesTargetTriggeredAbility( + Ability ability = new BecomesTargetSourceTriggeredAbility( new SacrificeSourceEffect().setText("sacrifice it"), StaticFilters.FILTER_SPELL_A ); ability.addEffect(new CreateTokenEffect(new ConstructToken(), xValue2) diff --git a/Mage.Sets/src/mage/cards/n/NakedSingularity.java b/Mage.Sets/src/mage/cards/n/NakedSingularity.java index 25a7e00a9d6..38508bde11d 100644 --- a/Mage.Sets/src/mage/cards/n/NakedSingularity.java +++ b/Mage.Sets/src/mage/cards/n/NakedSingularity.java @@ -95,12 +95,20 @@ class NakedSingularityEffect extends ReplacementEffectImpl { if (permanent.hasSubtype(SubType.FOREST, game)) { choice.getChoices().add("Black"); } + String chosenColor; if (choice.getChoices().size() == 1) { chosenColor = choice.getChoices().iterator().next(); + } else if (choice.getChoices().size() == 0) { + chosenColor = null; } else { - controller.choose(Outcome.PutManaInPool, choice, game); - chosenColor = choice.getChoice(); + // workaround to skip choose dialog in check playable state + if (game.inCheckPlayableState()) { + chosenColor = "Any"; + } else { + controller.choose(Outcome.PutManaInPool, choice, game); + chosenColor = choice.getChoice(); + } } if (chosenColor == null) { return false; @@ -123,6 +131,9 @@ class NakedSingularityEffect extends ReplacementEffectImpl { case "Green": mana.setToMana(Mana.GreenMana(amount)); break; + case "Any": + mana.setToMana(Mana.AnyMana(amount)); + break; } return false; } diff --git a/Mage.Sets/src/mage/cards/n/Nevermore.java b/Mage.Sets/src/mage/cards/n/Nevermore.java index 6ef54dfe2b9..a43c9036d69 100644 --- a/Mage.Sets/src/mage/cards/n/Nevermore.java +++ b/Mage.Sets/src/mage/cards/n/Nevermore.java @@ -14,7 +14,6 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.util.CardUtil; import java.util.UUID; @@ -57,24 +56,21 @@ class NevermoreEffect2 extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public NevermoreEffect2 copy() { return new NevermoreEffect2(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 (event.getType() == GameEvent.EventType.CAST_SPELL) { - MageObject object = game.getObject(event.getSourceId()); - String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - return CardUtil.haveSameNames(object, cardName, game); - } - return false; + MageObject object = game.getObject(event.getSourceId()); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return CardUtil.haveSameNames(object, cardName, game); } } diff --git a/Mage.Sets/src/mage/cards/n/NightmareShepherd.java b/Mage.Sets/src/mage/cards/n/NightmareShepherd.java index b4de651cda7..47f3b535724 100644 --- a/Mage.Sets/src/mage/cards/n/NightmareShepherd.java +++ b/Mage.Sets/src/mage/cards/n/NightmareShepherd.java @@ -91,9 +91,9 @@ class NightmareShepherdEffect extends OneShotEffect { false, null, 1, 1, false ); effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game) + 1)); - effect.setAdditionalSubType(SubType.NIGHTMARE); + effect.withAdditionalSubType(SubType.NIGHTMARE); player.moveCards(card, Zone.EXILED, source, game); effect.apply(game, source); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/n/NullChamber.java b/Mage.Sets/src/mage/cards/n/NullChamber.java index 2382a35820c..4d0203ed04e 100644 --- a/Mage.Sets/src/mage/cards/n/NullChamber.java +++ b/Mage.Sets/src/mage/cards/n/NullChamber.java @@ -111,11 +111,6 @@ class NullChamberReplacementEffect extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public NullChamberReplacementEffect copy() { return new NullChamberReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/o/OmenMachine.java b/Mage.Sets/src/mage/cards/o/OmenMachine.java index 01c165ebf88..517fb194f50 100644 --- a/Mage.Sets/src/mage/cards/o/OmenMachine.java +++ b/Mage.Sets/src/mage/cards/o/OmenMachine.java @@ -57,11 +57,6 @@ class OmenMachineEffect extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public OmenMachineEffect copy() { return new OmenMachineEffect(this); diff --git a/Mage.Sets/src/mage/cards/o/OpalineSliver.java b/Mage.Sets/src/mage/cards/o/OpalineSliver.java index 2ce3e6c9083..22dc99d327a 100644 --- a/Mage.Sets/src/mage/cards/o/OpalineSliver.java +++ b/Mage.Sets/src/mage/cards/o/OpalineSliver.java @@ -1,34 +1,30 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; 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.constants.*; import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.StackObject; +import mage.filter.FilterSpell; + +import java.util.UUID; /** - * - * @author anonymous + * @author xenohedron */ public final class OpalineSliver extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("All Slivers"); + private static final FilterPermanent filterSliver = new FilterPermanent("All Slivers"); + private static final FilterSpell filterSpell = new FilterSpell("a spell an opponent controls"); static { - filter.add(SubType.SLIVER.getPredicate()); + filterSliver.add(SubType.SLIVER.getPredicate()); + filterSpell.add(TargetController.OPPONENT.getControllerPredicate()); } public OpalineSliver(UUID ownerId, CardSetInfo setInfo) { @@ -38,9 +34,12 @@ public final class OpalineSliver extends CardImpl { this.toughness = new MageInt(2); // All Slivers have "Whenever this permanent becomes the target of a spell an opponent controls, you may draw a card." - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( - new OpalineSliverTriggeredAbility(), Duration.WhileOnBattlefield, - filter, "All Slivers have \"Whenever this permanent becomes the target of a spell an opponent controls, you may draw a card.\""))); + Ability gainedTriggeredAbility = new BecomesTargetSourceTriggeredAbility( + new DrawCardSourceControllerEffect(1), filterSpell, SetTargetPointer.NONE, true) + .setTriggerPhrase("Whenever this permanent becomes the target of a spell an opponent controls, "); + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + gainedTriggeredAbility, Duration.WhileOnBattlefield, filterSliver, + "All Slivers have \"Whenever this permanent becomes the target of a spell an opponent controls, you may draw a card.\""))); } private OpalineSliver(final OpalineSliver card) { @@ -52,43 +51,3 @@ public final class OpalineSliver extends CardImpl { return new OpalineSliver(this); } } - -class OpalineSliverTriggeredAbility extends TriggeredAbilityImpl { - - public OpalineSliverTriggeredAbility() { - super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false); - } - - private OpalineSliverTriggeredAbility(final OpalineSliverTriggeredAbility ability) { - super(ability); - } - - @Override - public OpalineSliverTriggeredAbility copy() { - return new OpalineSliverTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - StackObject spell = game.getStack().getStackObject(event.getSourceId()); - - if (spell == null) { - return false; - } else { - return event.getTargetId().equals(this.getSourceId()) - && game.getOpponents(this.controllerId).contains(event.getPlayerId()) - && StaticFilters.FILTER_SPELL_A.match(spell, getControllerId(), this, game); - } - } - - @Override - public String getRule() { - return "Whenever this permanent becomes the target of a spell an opponent controls, you may draw a card."; - } - -} diff --git a/Mage.Sets/src/mage/cards/o/Oubliette.java b/Mage.Sets/src/mage/cards/o/Oubliette.java index 3de70b1b995..a518b0d1449 100644 --- a/Mage.Sets/src/mage/cards/o/Oubliette.java +++ b/Mage.Sets/src/mage/cards/o/Oubliette.java @@ -102,11 +102,6 @@ class OubliettePhasePreventEffect extends ContinuousRuleModifyingEffectImpl { return event.getType() == GameEvent.EventType.PHASE_IN; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean applies(GameEvent event, Ability source, Game game) { return source.getSourcePermanentIfItStillExists(game) != null diff --git a/Mage.Sets/src/mage/cards/o/OutOfTime.java b/Mage.Sets/src/mage/cards/o/OutOfTime.java index 34dc4540538..4f88e185451 100644 --- a/Mage.Sets/src/mage/cards/o/OutOfTime.java +++ b/Mage.Sets/src/mage/cards/o/OutOfTime.java @@ -184,11 +184,6 @@ class OutOfTimeReplacementEffect extends ContinuousRuleModifyingEffectImpl { return event.getType() == GameEvent.EventType.PHASE_IN; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean applies(GameEvent event, Ability source, Game game) { Set creatureIds = (Set) game.getState().getValue("phasedOutCreatures" diff --git a/Mage.Sets/src/mage/cards/o/Overmaster.java b/Mage.Sets/src/mage/cards/o/Overmaster.java index f631e5230d9..1a6f33fdc89 100644 --- a/Mage.Sets/src/mage/cards/o/Overmaster.java +++ b/Mage.Sets/src/mage/cards/o/Overmaster.java @@ -71,11 +71,6 @@ class OvermasterEffect extends ContinuousRuleModifyingEffectImpl { } } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject sourceObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/o/OverwhelmingSplendor.java b/Mage.Sets/src/mage/cards/o/OverwhelmingSplendor.java index f34f7f6bf4a..d780cb83a54 100644 --- a/Mage.Sets/src/mage/cards/o/OverwhelmingSplendor.java +++ b/Mage.Sets/src/mage/cards/o/OverwhelmingSplendor.java @@ -1,4 +1,3 @@ - package mage.cards.o; import mage.MageObject; @@ -60,7 +59,7 @@ public final class OverwhelmingSplendor extends CardImpl { class OverwhelmingSplendorLoseAbilitiesEffect extends ContinuousEffectImpl { - public OverwhelmingSplendorLoseAbilitiesEffect() { + OverwhelmingSplendorLoseAbilitiesEffect() { super(Duration.WhileOnBattlefield, Outcome.LoseAbility); staticText = "Creatures enchanted player controls lose all abilities and have base power and toughness 1/1"; } @@ -76,21 +75,14 @@ class OverwhelmingSplendorLoseAbilitiesEffect extends ContinuousEffectImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (enchantment == null) { - return false; - } + return false; } - Player player = game.getPlayer(enchantment.getAttachedTo()); if (player == null) { return false; } - for (Permanent permanent : game.getState().getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, player.getId(), game)) { switch (layer) { case AbilityAddingRemovingEffects_6: @@ -120,7 +112,7 @@ class OverwhelmingSplendorLoseAbilitiesEffect extends ContinuousEffectImpl { class OverwhelmingSplendorCantActivateEffect extends ContinuousRuleModifyingEffectImpl { - public OverwhelmingSplendorCantActivateEffect() { + OverwhelmingSplendorCantActivateEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); staticText = "Enchanted player can't activate abilities that aren't mana abilities or loyalty abilities"; } @@ -134,11 +126,6 @@ class OverwhelmingSplendorCantActivateEffect extends ContinuousRuleModifyingEffe return new OverwhelmingSplendorCantActivateEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -149,11 +136,12 @@ class OverwhelmingSplendorCantActivateEffect extends ContinuousRuleModifyingEffe } @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() != GameEvent.EventType.ACTIVATE_ABILITY) { - return false; - } + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; + } + @Override + public boolean applies(GameEvent event, Ability source, Game game) { Permanent enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (enchantment == null || !event.getPlayerId().equals(enchantment.getAttachedTo())) { return false; diff --git a/Mage.Sets/src/mage/cards/p/PardicMiner.java b/Mage.Sets/src/mage/cards/p/PardicMiner.java index 1b44db6bce8..673e8949ab3 100644 --- a/Mage.Sets/src/mage/cards/p/PardicMiner.java +++ b/Mage.Sets/src/mage/cards/p/PardicMiner.java @@ -1,7 +1,6 @@ package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -10,23 +9,20 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; 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.target.TargetPlayer; +import java.util.UUID; + /** - * * @author cbt33 */ public final class PardicMiner extends CardImpl { public PardicMiner(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.DWARF); this.power = new MageInt(1); @@ -64,11 +60,6 @@ class PardicMinerEffect extends ContinuousRuleModifyingEffectImpl { return new PardicMinerEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -79,11 +70,12 @@ class PardicMinerEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.PLAY_LAND && event.getPlayerId().equals(source.getFirstTarget())) { - return true; - } - return false; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.PLAY_LAND; } + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getPlayerId().equals(source.getFirstTarget()); + } } diff --git a/Mage.Sets/src/mage/cards/p/ParnesseTheSubtleBrush.java b/Mage.Sets/src/mage/cards/p/ParnesseTheSubtleBrush.java index 6e8267d3d5e..528fc9882b0 100644 --- a/Mage.Sets/src/mage/cards/p/ParnesseTheSubtleBrush.java +++ b/Mage.Sets/src/mage/cards/p/ParnesseTheSubtleBrush.java @@ -3,13 +3,14 @@ package mage.cards.p; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.TargetOfOpponentsSpellOrAbilityTriggeredAbility; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.costs.common.PayLifeCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; @@ -35,7 +36,8 @@ public class ParnesseTheSubtleBrush extends CardImpl { // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, // counter that spell or ability unless that player pays 4 life. - this.addAbility(new TargetOfOpponentsSpellOrAbilityTriggeredAbility(new CounterUnlessPaysEffect(new PayLifeCost(4).setText("4 life")))); + this.addAbility(new BecomesTargetControllerTriggeredAbility(new CounterUnlessPaysEffect(new PayLifeCost(4).setText("4 life")), + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); // Whenever you copy a spell, up to one target opponent may also copy that spell. // They may choose new targets for that copy. diff --git a/Mage.Sets/src/mage/cards/p/PeaceTalks.java b/Mage.Sets/src/mage/cards/p/PeaceTalks.java index 49aff84201c..ef4bf0b82d6 100644 --- a/Mage.Sets/src/mage/cards/p/PeaceTalks.java +++ b/Mage.Sets/src/mage/cards/p/PeaceTalks.java @@ -151,11 +151,6 @@ class PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities ex return false; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities copy() { return new PeaceTalksPlayersAndPermanentsCantBeTargetsOfSpellsOrActivatedAbilities(this); diff --git a/Mage.Sets/src/mage/cards/p/Petrahydrox.java b/Mage.Sets/src/mage/cards/p/Petrahydrox.java index 04f81ab1ddc..270d27a3ae2 100644 --- a/Mage.Sets/src/mage/cards/p/Petrahydrox.java +++ b/Mage.Sets/src/mage/cards/p/Petrahydrox.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class Petrahydrox extends CardImpl { this.toughness = new MageInt(3); // When Petrahydrox becomes the target of a spell or ability, return Petrahydrox to its owner's hand. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new ReturnToHandSourceEffect(true))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new ReturnToHandSourceEffect(true))); } private Petrahydrox(final Petrahydrox card) { diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalAbomination.java b/Mage.Sets/src/mage/cards/p/PhantasmalAbomination.java index bf574189fac..ac11be77424 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalAbomination.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalAbomination.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.cards.CardImpl; @@ -25,7 +25,7 @@ public final class PhantasmalAbomination extends CardImpl { this.toughness = new MageInt(5); this.addAbility(DefenderAbility.getInstance()); - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private PhantasmalAbomination(final PhantasmalAbomination card) { diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalBear.java b/Mage.Sets/src/mage/cards/p/PhantasmalBear.java index c3307300c86..7ad399cabb1 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalBear.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalBear.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class PhantasmalBear extends CardImpl { this.toughness = new MageInt(2); // When Phantasmal Bear becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private PhantasmalBear(final PhantasmalBear card) { diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalDragon.java b/Mage.Sets/src/mage/cards/p/PhantasmalDragon.java index 7c5033d5a40..f1fce0b8755 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalDragon.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalDragon.java @@ -3,7 +3,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class PhantasmalDragon extends CardImpl { this.toughness = new MageInt(5); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private PhantasmalDragon(final PhantasmalDragon card) { diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalDreadmaw.java b/Mage.Sets/src/mage/cards/p/PhantasmalDreadmaw.java index c2c6098bce7..7333117217f 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalDreadmaw.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalDreadmaw.java @@ -1,7 +1,7 @@ package mage.cards.p; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class PhantasmalDreadmaw extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // When Phantasmal Dreadmaw becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private PhantasmalDreadmaw(final PhantasmalDreadmaw card) { diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalImage.java b/Mage.Sets/src/mage/cards/p/PhantasmalImage.java index b8830a895a1..9466bd0b5c3 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalImage.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalImage.java @@ -4,7 +4,7 @@ package mage.cards.p; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CopyPermanentEffect; @@ -31,7 +31,7 @@ public final class PhantasmalImage extends CardImpl { public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyToObjectId) { // Add directly because the created permanent is only used to copy from, so there is no need to add the ability to e.g. TriggeredAbilities blueprint.addSubType(SubType.ILLUSION); - blueprint.getAbilities().add(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + blueprint.getAbilities().add(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); return true; } }; diff --git a/Mage.Sets/src/mage/cards/p/PhantomBeast.java b/Mage.Sets/src/mage/cards/p/PhantomBeast.java index 3a9ffaeca91..0af4e336330 100644 --- a/Mage.Sets/src/mage/cards/p/PhantomBeast.java +++ b/Mage.Sets/src/mage/cards/p/PhantomBeast.java @@ -4,7 +4,7 @@ package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class PhantomBeast extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(5); - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private PhantomBeast(final PhantomBeast card) { diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianCensor.java b/Mage.Sets/src/mage/cards/p/PhyrexianCensor.java index 6ab491c7231..1e1751f2be8 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianCensor.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianCensor.java @@ -79,11 +79,6 @@ class PhyrexianCensorEffect extends ContinuousRuleModifyingEffectImpl { return new PhyrexianCensorEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java b/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java index ac80d07cd3d..9965bac5cb2 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianRevoker.java @@ -1,6 +1,5 @@ package mage.cards.p; -import java.util.Optional; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -15,6 +14,8 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.util.CardUtil; + +import java.util.Optional; import java.util.UUID; /** @@ -57,11 +58,6 @@ class PhyrexianRevokerEffect2 extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public PhyrexianRevokerEffect2 copy() { return new PhyrexianRevokerEffect2(this); @@ -76,18 +72,21 @@ class PhyrexianRevokerEffect2 extends ContinuousRuleModifyingEffectImpl { return null; } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.ACTIVATE_ABILITY; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.ACTIVATE_ABILITY) { - MageObject object = game.getObject(event.getSourceId()); // Can happen for special ability???? - if (object != null) { - Optional optAbility = object.getAbilities().get(event.getTargetId()); - if (optAbility.isPresent() && AbilityType.SPECIAL_ACTION == optAbility.get().getAbilityType()) { - return false; - } - String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - return CardUtil.haveSameNames(object, cardName, game); + MageObject object = game.getObject(event.getSourceId()); // Can happen for special ability???? + if (object != null) { + Optional optAbility = object.getAbilities().get(event.getTargetId()); + if (optAbility.isPresent() && AbilityType.SPECIAL_ACTION == optAbility.get().getAbilityType()) { + return false; } + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return CardUtil.haveSameNames(object, cardName, game); } return false; } diff --git a/Mage.Sets/src/mage/cards/p/PillarOfTheParunsCustom.java b/Mage.Sets/src/mage/cards/p/PillarOfTheParunsCustom.java new file mode 100644 index 00000000000..e933679273a --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PillarOfTheParunsCustom.java @@ -0,0 +1,46 @@ + +package mage.cards.p; + +import mage.abilities.keyword.HexproofAbility; +import mage.abilities.mana.ColorlessManaAbility; +import mage.abilities.mana.ConditionalAnyColorManaAbility; +import mage.abilities.mana.conditional.ConditionalSpellManaBuilder; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * Custom version of Pillar of the Paruns. + * Tweaked for the needs of the Pillar of the Paruns custom mode. + * + * Has Hexproof and "{T}: Add {1}" + * + * @author Susucr + */ +public final class PillarOfTheParunsCustom extends CardImpl { + + public PillarOfTheParunsCustom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Custom: Hexproof + this.addAbility(HexproofAbility.getInstance()); + + // Custom: {T}: Add {1} + this.addAbility(new ColorlessManaAbility()); + + // {T}: Add one mana of any color. Spend this mana only to cast a multicolored spell. + this.addAbility(new ConditionalAnyColorManaAbility(1, new ConditionalSpellManaBuilder(StaticFilters.FILTER_SPELL_A_MULTICOLORED))); + } + + private PillarOfTheParunsCustom(final PillarOfTheParunsCustom card) { + super(card); + } + + @Override + public PillarOfTheParunsCustom copy() { + return new PillarOfTheParunsCustom(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PithingNeedle.java b/Mage.Sets/src/mage/cards/p/PithingNeedle.java index b1964ba990d..946c37c5dde 100644 --- a/Mage.Sets/src/mage/cards/p/PithingNeedle.java +++ b/Mage.Sets/src/mage/cards/p/PithingNeedle.java @@ -61,11 +61,6 @@ class PithingNeedleEffect extends ContinuousRuleModifyingEffectImpl { return new PithingNeedleEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; diff --git a/Mage.Sets/src/mage/cards/p/PlatinumAngel.java b/Mage.Sets/src/mage/cards/p/PlatinumAngel.java index fad0547024b..5686f4f0391 100644 --- a/Mage.Sets/src/mage/cards/p/PlatinumAngel.java +++ b/Mage.Sets/src/mage/cards/p/PlatinumAngel.java @@ -39,37 +39,38 @@ public final class PlatinumAngel extends CardImpl { public PlatinumAngel copy() { return new PlatinumAngel(this); } +} - static class PlatinumAngelEffect extends ContinuousRuleModifyingEffectImpl { +class PlatinumAngelEffect extends ContinuousRuleModifyingEffectImpl { - public PlatinumAngelEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit, false, false); - staticText = "You can't lose the game and your opponents can't win the game"; - } - - private PlatinumAngelEffect(final PlatinumAngelEffect effect) { - super(effect); - } - - @Override - public PlatinumAngelEffect copy() { - return new PlatinumAngelEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - switch (event.getType()) { - case WINS: - return game.getOpponents(source.getControllerId()).contains(event.getPlayerId()); - case LOSES: - return source.isControlledBy(event.getPlayerId()); - } - return false; + public PlatinumAngelEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit, false, false); + staticText = "You can't lose the game and your opponents can't win the game"; + } + + private PlatinumAngelEffect(final PlatinumAngelEffect effect) { + super(effect); + } + + @Override + public PlatinumAngelEffect copy() { + return new PlatinumAngelEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.WINS + || event.getType() == GameEvent.EventType.LOSES; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + switch (event.getType()) { + case WINS: + return game.getOpponents(source.getControllerId()).contains(event.getPlayerId()); + case LOSES: + return source.isControlledBy(event.getPlayerId()); } + return false; } } diff --git a/Mage.Sets/src/mage/cards/p/PreyseizerDragon.java b/Mage.Sets/src/mage/cards/p/PreyseizerDragon.java index 06227dcff2d..7d4693219d0 100644 --- a/Mage.Sets/src/mage/cards/p/PreyseizerDragon.java +++ b/Mage.Sets/src/mage/cards/p/PreyseizerDragon.java @@ -37,7 +37,8 @@ public final class PreyseizerDragon extends CardImpl { this.addAbility(new DevourAbility(2)); // Whenever Preyseizer Dragon attacks, it deals damage to any target equal to the number of +1/+1 counters on Preyseizer Dragon. - Ability ability = new AttacksTriggeredAbility(new DamageTargetEffect(new CountersSourceCount(CounterType.P1P1)), false); + Ability ability = new AttacksTriggeredAbility(new DamageTargetEffect(new CountersSourceCount(CounterType.P1P1)) + .setText("it deals damage to any target equal to the number of +1/+1 counters on {this}"), false); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PriestOfTitania.java b/Mage.Sets/src/mage/cards/p/PriestOfTitania.java index 25d6e7c4949..9cf058d70ff 100644 --- a/Mage.Sets/src/mage/cards/p/PriestOfTitania.java +++ b/Mage.Sets/src/mage/cards/p/PriestOfTitania.java @@ -6,13 +6,13 @@ import mage.MageInt; import mage.Mana; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.hint.ValueHint; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; /** * @author BetaSteward_at_googlemail.com, North @@ -31,7 +31,7 @@ public final class PriestOfTitania extends CardImpl { this.toughness = new MageInt(1); // {T}: Add {G} for each Elf on the battlefield. - this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), xValue)); + this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), xValue).addHint(new ValueHint("Elves on the battlefield", xValue))); } private PriestOfTitania(final PriestOfTitania card) { diff --git a/Mage.Sets/src/mage/cards/r/RakdosLordOfRiots.java b/Mage.Sets/src/mage/cards/r/RakdosLordOfRiots.java index 1a2b65b7446..594fcf58303 100644 --- a/Mage.Sets/src/mage/cards/r/RakdosLordOfRiots.java +++ b/Mage.Sets/src/mage/cards/r/RakdosLordOfRiots.java @@ -65,11 +65,6 @@ class RakdosLordOfRiotsCantCastEffect extends ContinuousRuleModifyingEffectImpl super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public RakdosLordOfRiotsCantCastEffect copy() { return new RakdosLordOfRiotsCantCastEffect(this); diff --git a/Mage.Sets/src/mage/cards/r/RangerCaptainOfEos.java b/Mage.Sets/src/mage/cards/r/RangerCaptainOfEos.java index 5e17f7dec7c..25d88f94b05 100644 --- a/Mage.Sets/src/mage/cards/r/RangerCaptainOfEos.java +++ b/Mage.Sets/src/mage/cards/r/RangerCaptainOfEos.java @@ -78,11 +78,6 @@ class RangerCaptainOfEosEffect extends ContinuousRuleModifyingEffectImpl { return new RangerCaptainOfEosEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/r/RasaadYnBashir.java b/Mage.Sets/src/mage/cards/r/RasaadYnBashir.java index 45ef52ba1d6..b11179fac0c 100644 --- a/Mage.Sets/src/mage/cards/r/RasaadYnBashir.java +++ b/Mage.Sets/src/mage/cards/r/RasaadYnBashir.java @@ -9,7 +9,7 @@ import mage.abilities.condition.common.HaveInitiativeCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessAllEffect; +import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -38,7 +38,7 @@ public final class RasaadYnBashir extends CardImpl { this.toughness = new MageInt(3); // Each creature you control assigns combat damage equal to its toughness rather than its power. - this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURE_EACH))); + this.addAbility(new SimpleStaticAbility(new CombatDamageByToughnessControlledEffect())); // Whenever Rasaad yn Bashir attacks, if you have the initiative, double the toughness of each creature you control until end of turn. this.addAbility(new ConditionalInterveningIfTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/r/RatadrabikOfUrborg.java b/Mage.Sets/src/mage/cards/r/RatadrabikOfUrborg.java index 9e858ba6fbe..45cea3b9955 100644 --- a/Mage.Sets/src/mage/cards/r/RatadrabikOfUrborg.java +++ b/Mage.Sets/src/mage/cards/r/RatadrabikOfUrborg.java @@ -99,7 +99,7 @@ class RatadrabikOfUrborgEffect extends OneShotEffect { return false; } CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(controller.getId(), null, false,1,false,false,null,2,2,false); - effect.setAdditionalSubType(SubType.ZOMBIE); + effect.withAdditionalSubType(SubType.ZOMBIE); effect.setIsntLegendary(true); effect.setTargetPointer(new FixedTarget(copyFrom.getId(),game)); ObjectColor colors = copyFrom.getColor(); diff --git a/Mage.Sets/src/mage/cards/r/RayneAcademyChancellor.java b/Mage.Sets/src/mage/cards/r/RayneAcademyChancellor.java index a2a49b2c2e4..03ef29027f5 100644 --- a/Mage.Sets/src/mage/cards/r/RayneAcademyChancellor.java +++ b/Mage.Sets/src/mage/cards/r/RayneAcademyChancellor.java @@ -2,7 +2,7 @@ package mage.cards.r; import mage.MageInt; -import mage.abilities.common.TargetOfOpponentsSpellOrAbilityTriggeredAbility; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.condition.common.EnchantedSourceCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.Effect; @@ -10,8 +10,10 @@ 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.SubType; import mage.constants.SuperType; +import mage.filter.StaticFilters; import java.util.UUID; @@ -38,7 +40,8 @@ public final class RayneAcademyChancellor extends CardImpl { new EnchantedSourceCondition(), "you may draw a card. You may draw an additional card if {this} is enchanted" ); - this.addAbility(new TargetOfOpponentsSpellOrAbilityTriggeredAbility(drawEffect, true, false)); + this.addAbility(new BecomesTargetControllerTriggeredAbility(drawEffect, + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, true)); } private RayneAcademyChancellor(final RayneAcademyChancellor card) { diff --git a/Mage.Sets/src/mage/cards/r/RealitySmasher.java b/Mage.Sets/src/mage/cards/r/RealitySmasher.java index 35d367dea82..3c0222f83dc 100644 --- a/Mage.Sets/src/mage/cards/r/RealitySmasher.java +++ b/Mage.Sets/src/mage/cards/r/RealitySmasher.java @@ -1,9 +1,7 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.keyword.HasteAbility; @@ -11,21 +9,24 @@ import mage.abilities.keyword.TrampleAbility; 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.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.target.targetpointer.FixedTarget; +import mage.constants.TargetController; +import mage.filter.FilterSpell; + +import java.util.UUID; /** * - * @author LevelX2 + * @author LevelX2, xenohedron */ public final class RealitySmasher extends CardImpl { + private static final FilterSpell filter = new FilterSpell("a spell an opponent controls"); + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + public RealitySmasher(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{C}"); this.subtype.add(SubType.ELDRAZI); @@ -37,7 +38,10 @@ public final class RealitySmasher extends CardImpl { // Haste this.addAbility(HasteAbility.getInstance()); // Whenever Reality Smasher becomes the target of a spell an opponent controls, counter that spell unless its controller discards a card. - this.addAbility(new RealitySmasherTriggeredAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility( + new CounterUnlessPaysEffect(new DiscardCardCost()).setText("counter that spell unless its controller discards a card"), + filter, SetTargetPointer.SPELL, false + )); } private RealitySmasher(final RealitySmasher card) { @@ -49,45 +53,3 @@ public final class RealitySmasher extends CardImpl { return new RealitySmasher(this); } } - -class RealitySmasherTriggeredAbility extends TriggeredAbilityImpl { - - public RealitySmasherTriggeredAbility() { - super(Zone.BATTLEFIELD, new CounterUnlessPaysEffect(new DiscardCardCost()), false); - } - - private RealitySmasherTriggeredAbility(final RealitySmasherTriggeredAbility ability) { - super(ability); - } - - @Override - public RealitySmasherTriggeredAbility copy() { - return new RealitySmasherTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - StackObject spell = game.getStack().getStackObject(event.getSourceId()); - if (!(spell instanceof Spell)) { - return false; - } else { - if (event.getTargetId().equals(this.getSourceId()) - && game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - getEffects().setTargetPointer(new FixedTarget(spell.getId())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell an opponent controls, counter that spell unless its controller discards a card."; - } - -} diff --git a/Mage.Sets/src/mage/cards/r/RealityTwist.java b/Mage.Sets/src/mage/cards/r/RealityTwist.java index 669cfb695e6..52fc3ab1f80 100644 --- a/Mage.Sets/src/mage/cards/r/RealityTwist.java +++ b/Mage.Sets/src/mage/cards/r/RealityTwist.java @@ -95,9 +95,16 @@ class RealityTwistEffect extends ReplacementEffectImpl { String chosenColor; if (choice.getChoices().size() == 1) { chosenColor = choice.getChoices().iterator().next(); + } else if (choice.getChoices().size() == 0) { + chosenColor = null; } else { - controller.choose(Outcome.PutManaInPool, choice, game); - chosenColor = choice.getChoice(); + // workaround to skip choose dialog in check playable state + if (game.inCheckPlayableState()) { + chosenColor = "Any"; + } else { + controller.choose(Outcome.PutManaInPool, choice, game); + chosenColor = choice.getChoice(); + } } if (chosenColor == null) { return false; @@ -117,6 +124,9 @@ class RealityTwistEffect extends ReplacementEffectImpl { case "Green": mana.setToMana(Mana.GreenMana(amount)); break; + case "Any": + mana.setToMana(Mana.AnyMana(amount)); + break; } return false; } diff --git a/Mage.Sets/src/mage/cards/r/ReflectorMage.java b/Mage.Sets/src/mage/cards/r/ReflectorMage.java index ba263f21c68..2ff8fe08be0 100644 --- a/Mage.Sets/src/mage/cards/r/ReflectorMage.java +++ b/Mage.Sets/src/mage/cards/r/ReflectorMage.java @@ -124,11 +124,6 @@ class ExclusionRitualReplacementEffect extends ContinuousRuleModifyingEffectImpl return CardUtil.haveSameNames(card, creatureName, game) && Objects.equals(ownerId, card.getOwnerId()); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public ExclusionRitualReplacementEffect copy() { return new ExclusionRitualReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java b/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java index 89b364664a5..de5bd20a20c 100644 --- a/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java +++ b/Mage.Sets/src/mage/cards/r/ReidaneGodOfTheWorthy.java @@ -3,7 +3,7 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.SpellAbility; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.PreventionEffectImpl; @@ -17,12 +17,13 @@ import mage.cards.CardSetInfo; import mage.cards.ModalDoubleFacedCard; import mage.constants.*; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; -import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; import java.util.UUID; @@ -33,10 +34,12 @@ import java.util.UUID; public final class ReidaneGodOfTheWorthy extends ModalDoubleFacedCard { private static final FilterPermanent filter = new FilterLandPermanent("snow lands your opponents control"); + private static final FilterPermanent filterAnother = new FilterControlledPermanent("another permanent you control"); static { filter.add(SuperType.SNOW.getPredicate()); filter.add(TargetController.OPPONENT.getControllerPredicate()); + filterAnother.add(AnotherPredicate.instance); } public ReidaneGodOfTheWorthy(UUID ownerId, CardSetInfo setInfo) { @@ -70,8 +73,10 @@ public final class ReidaneGodOfTheWorthy extends ModalDoubleFacedCard { // If a source an opponent controls would deal damage to you or a permanent you control, prevent 1 of that damage. this.getRightHalfCard().addAbility(new SimpleStaticAbility(new ValkmiraProtectorsShieldPreventionEffect())); - // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {1}. - this.getRightHalfCard().addAbility(new ValkmiraProtectorsShieldTriggeredAbility()); + // Whenever you or another permanent you control becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {1}. + this.getRightHalfCard().addAbility(new BecomesTargetControllerTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(1)) + .setText("counter that spell or ability unless its controller pays {1}"), + filterAnother, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); } private ReidaneGodOfTheWorthy(final ReidaneGodOfTheWorthy card) { @@ -158,51 +163,3 @@ class ValkmiraProtectorsShieldPreventionEffect extends PreventionEffectImpl { return new ValkmiraProtectorsShieldPreventionEffect(this); } } - -class ValkmiraProtectorsShieldTriggeredAbility extends TriggeredAbilityImpl { - - ValkmiraProtectorsShieldTriggeredAbility() { - super(Zone.BATTLEFIELD, new CounterUnlessPaysEffect(new GenericManaCost(1))); - } - - private ValkmiraProtectorsShieldTriggeredAbility(final ValkmiraProtectorsShieldTriggeredAbility ability) { - super(ability); - } - - @Override - public ValkmiraProtectorsShieldTriggeredAbility copy() { - return new ValkmiraProtectorsShieldTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(getSourceId())) { - return false; - } - StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); - if (stackObject == null || !game.getOpponents(getControllerId()).contains(stackObject.getControllerId())) { - return false; - } - if (isControlledBy(event.getTargetId())) { - this.getEffects().setTargetPointer(new FixedTarget(stackObject.getId(), game)); - return true; - } - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent == null || !permanent.isControlledBy(getControllerId())) { - return false; - } - this.getEffects().setTargetPointer(new FixedTarget(stackObject.getId(), game)); - return true; - } - - @Override - public String getRule() { - return "Whenever you or another permanent you control becomes the target of a spell or ability " + - "an opponent controls, counter that spell or ability unless its controller pays {1}."; - } -} diff --git a/Mage.Sets/src/mage/cards/r/RenderSilent.java b/Mage.Sets/src/mage/cards/r/RenderSilent.java index 3674bcb59e1..2f2bc0e5c33 100644 --- a/Mage.Sets/src/mage/cards/r/RenderSilent.java +++ b/Mage.Sets/src/mage/cards/r/RenderSilent.java @@ -91,11 +91,6 @@ class RenderSilentEffect extends ContinuousRuleModifyingEffectImpl { return new RenderSilentEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/r/Retromancer.java b/Mage.Sets/src/mage/cards/r/Retromancer.java index f7cc0d679ea..75c50b888c0 100644 --- a/Mage.Sets/src/mage/cards/r/Retromancer.java +++ b/Mage.Sets/src/mage/cards/r/Retromancer.java @@ -1,25 +1,19 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; 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.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author L_J + * @author xenohedron */ public final class Retromancer extends CardImpl { @@ -31,7 +25,10 @@ public final class Retromancer extends CardImpl { this.toughness = new MageInt(3); // Whenever Retromancer becomes the target of a spell or ability, Retromancer deals 3 damage to that spell or ability's controller. - this.addAbility(new RetromancerTriggeredAbility(new DamageTargetEffect(3))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new DamageTargetEffect(3) + .setText("{this} deals 3 damage to that spell or ability's controller"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.PLAYER, false) + .withRuleTextReplacement(false)); } private Retromancer(final Retromancer card) { @@ -43,41 +40,3 @@ public final class Retromancer extends CardImpl { return new Retromancer(this); } } - -class RetromancerTriggeredAbility extends TriggeredAbilityImpl { - - public RetromancerTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private RetromancerTriggeredAbility(final RetromancerTriggeredAbility ability) { - super(ability); - } - - @Override - public RetromancerTriggeredAbility copy() { - return new RetromancerTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature != null && event.getTargetId().equals(getSourceId())) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getPlayerId())); - } - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability, {this} deals 3 damage to that spell or ability's controller."; - } -} diff --git a/Mage.Sets/src/mage/cards/r/RhysTheExiled.java b/Mage.Sets/src/mage/cards/r/RhysTheExiled.java index 1ca69a348b2..7c05f5b65e5 100644 --- a/Mage.Sets/src/mage/cards/r/RhysTheExiled.java +++ b/Mage.Sets/src/mage/cards/r/RhysTheExiled.java @@ -12,6 +12,7 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -38,7 +39,8 @@ public final class RhysTheExiled extends CardImpl { // Whenever Rhys the Exiled attacks, you gain 1 life for each Elf you control. this.addAbility(new AttacksTriggeredAbility(new GainLifeEffect(xValue) - .setText("you gain 1 life for each Elf you control"), false)); + .setText("you gain 1 life for each Elf you control"), false) + .addHint(new ValueHint("Elves you control", xValue))); // {B}, Sacrifice an Elf: Regenerate Rhys the Exiled. Ability ability = new SimpleActivatedAbility(new RegenerateSourceEffect(), new ManaCostsImpl<>("{B}")); diff --git a/Mage.Sets/src/mage/cards/r/RidersOfTheMark.java b/Mage.Sets/src/mage/cards/r/RidersOfTheMark.java new file mode 100644 index 00000000000..11fe9b1d576 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RidersOfTheMark.java @@ -0,0 +1,123 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.AttackedThisTurnSourceCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.HumanSoldierToken; +import mage.players.Player; +import mage.watchers.common.AttackedThisTurnWatcher; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class RidersOfTheMark extends CardImpl { + private static final FilterPermanent filter + = new FilterControlledPermanent(SubType.HUMAN, "Human you control"); + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint = new ValueHint("Humans you control", xValue); + + public RidersOfTheMark(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(7); + this.toughness = new MageInt(4); + + // This spell costs {1} less to cast for each Human you control. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, + new SpellCostReductionForEachSourceEffect(1, xValue) + .setCanWorksOnStackOnly(true) + ).setRuleAtTheTop(true).addHint(hint)); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // At the beginning of your end step, if Riders of the Mark attacked this turn, return it to its owner's hand. If you do, create a number of 1/1 white Human Soldier creature tokens equal to its toughness. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new RidersOfTheMarkEffect(), + TargetController.YOU, + false + ), + AttackedThisTurnSourceCondition.instance, + "At the beginning of your end step, if {this} attacked this turn, " + + "return it to its owner's hand. If you do, create a number of " + + "1/1 white Human Soldier creature tokens equal to its toughness." + )); + + this.getSpellAbility().addWatcher(new AttackedThisTurnWatcher()); + } + + private RidersOfTheMark(final RidersOfTheMark card) { + super(card); + } + + @Override + public RidersOfTheMark copy() { + return new RidersOfTheMark(this); + } +} + +class RidersOfTheMarkEffect extends OneShotEffect { + + RidersOfTheMarkEffect() { + super(Outcome.Benefit); + staticText = "return it to its owner's hand. If you do, create a number of " + + "1/1 white Human Soldier creature tokens equal to its toughness."; + } + + private RidersOfTheMarkEffect(final RidersOfTheMarkEffect effect) { + super(effect); + } + + @Override + public RidersOfTheMarkEffect copy() { + return new RidersOfTheMarkEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + Player player = game.getPlayer(source.getControllerId()); + if (permanent == null || player == null) { + return false; + } + int toughness = permanent.getToughness().getValue(); + if (!player.moveCards(permanent, Zone.HAND, source, game)) { + return false; + } + + if (toughness > 0) { + new CreateTokenEffect(new HumanSoldierToken(), toughness) + .apply(game, source); + } + return true; + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/r/RocketTrooper.java b/Mage.Sets/src/mage/cards/r/RocketTrooper.java index 69316d4911e..7f1c0a20cdc 100644 --- a/Mage.Sets/src/mage/cards/r/RocketTrooper.java +++ b/Mage.Sets/src/mage/cards/r/RocketTrooper.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -6,7 +5,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.cards.CardImpl; @@ -14,7 +12,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetOpponentsCreaturePermanent; @@ -24,6 +21,8 @@ import mage.target.common.TargetOpponentsCreaturePermanent; */ public final class RocketTrooper extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.TROOPER, "Trooper creatures"); + public RocketTrooper(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.subtype.add(SubType.HUMAN); @@ -32,13 +31,10 @@ public final class RocketTrooper extends CardImpl { this.toughness = new MageInt(2); // Trooper creatures you control have "Whenever this creature enters the battlefield, it deals 1 damage to target creature an opponent controls". - Effect effect = new DamageTargetEffect(1); - effect.setText("Whenever this creature enters the battlefield, it deals 1 damage to target creature an opponent controls"); - Ability ability = new EntersBattlefieldTriggeredAbility(effect, false, true); + Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(1, "it"), false) + .setTriggerPhrase("When this creature enters the battlefield, "); ability.addTarget(new TargetOpponentsCreaturePermanent()); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, - new GainAbilityControlledEffect(ability, Duration.WhileOnBattlefield, - new FilterCreaturePermanent(SubType.TROOPER, "Trooper creatures"), false))); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(ability, Duration.WhileOnBattlefield, filter, false))); } diff --git a/Mage.Sets/src/mage/cards/r/RofellosLlanowarEmissary.java b/Mage.Sets/src/mage/cards/r/RofellosLlanowarEmissary.java index c7cff50ec12..5e398b4a149 100644 --- a/Mage.Sets/src/mage/cards/r/RofellosLlanowarEmissary.java +++ b/Mage.Sets/src/mage/cards/r/RofellosLlanowarEmissary.java @@ -6,6 +6,7 @@ import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.hint.ValueHint; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -36,7 +37,9 @@ public final class RofellosLlanowarEmissary extends CardImpl { this.toughness = new MageInt(1); // {tap}: Add {G} for each Forest you control. - this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), new PermanentsOnBattlefieldCount(filter))); + this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), new PermanentsOnBattlefieldCount(filter)) + .addHint(new ValueHint("Forests you control", new PermanentsOnBattlefieldCount(filter))) + ); } private RofellosLlanowarEmissary(final RofellosLlanowarEmissary card) { diff --git a/Mage.Sets/src/mage/cards/r/RootSliver.java b/Mage.Sets/src/mage/cards/r/RootSliver.java index 7af0d8f4e56..cf9e1806603 100644 --- a/Mage.Sets/src/mage/cards/r/RootSliver.java +++ b/Mage.Sets/src/mage/cards/r/RootSliver.java @@ -65,11 +65,6 @@ class RootSliverEffect extends ContinuousRuleModifyingEffectImpl { return new RootSliverEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.COUNTER; diff --git a/Mage.Sets/src/mage/cards/r/Runesword.java b/Mage.Sets/src/mage/cards/r/Runesword.java index 2c19848284e..51c6dfce7c8 100644 --- a/Mage.Sets/src/mage/cards/r/Runesword.java +++ b/Mage.Sets/src/mage/cards/r/Runesword.java @@ -123,11 +123,6 @@ class RuneswordCantBeRegeneratedEffect extends ContinuousRuleModifyingEffectImpl targetCreatureId = getTargetPointer().getFirst(game, source); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.REGENERATE; diff --git a/Mage.Sets/src/mage/cards/s/SanctumPrelate.java b/Mage.Sets/src/mage/cards/s/SanctumPrelate.java index 2d6779ac475..28e99a2e661 100644 --- a/Mage.Sets/src/mage/cards/s/SanctumPrelate.java +++ b/Mage.Sets/src/mage/cards/s/SanctumPrelate.java @@ -92,11 +92,6 @@ class SanctumPrelateReplacementEffect extends ContinuousRuleModifyingEffectImpl super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public SanctumPrelateReplacementEffect copy() { return new SanctumPrelateReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java b/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java index f394966abe3..37c5ed94f69 100644 --- a/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java +++ b/Mage.Sets/src/mage/cards/s/SasayaOrochiAscendant.java @@ -226,14 +226,21 @@ class SasayasEssenceManaEffect extends ManaEffect { for (int i = 0; i < count; i++) { choice.clearChoice(); + String chosenColor; if (choice.getChoices().size() == 1) { - choice.setChoice(choice.getChoices().iterator().next()); + chosenColor = choice.getChoices().iterator().next(); } else { - if (!controller.choose(outcome, choice, game)) { - return newMana; + // workaround to skip choose dialog in check playable state + if (game.inCheckPlayableState()) { + chosenColor = "Any"; + } else { + if (!controller.choose(Outcome.PutManaInPool, choice, game)) { + return newMana; + } + chosenColor = choice.getChoice(); } } - switch (choice.getChoice()) { + switch (chosenColor) { case "Black": newMana.increaseBlack(); break; @@ -252,6 +259,9 @@ class SasayasEssenceManaEffect extends ManaEffect { case "Colorless": newMana.increaseColorless(); break; + case "Any": + newMana.increaseAny(); + break; } } diff --git a/Mage.Sets/src/mage/cards/s/SavageSummoning.java b/Mage.Sets/src/mage/cards/s/SavageSummoning.java index e71b265212d..e10ba2dd44b 100644 --- a/Mage.Sets/src/mage/cards/s/SavageSummoning.java +++ b/Mage.Sets/src/mage/cards/s/SavageSummoning.java @@ -23,7 +23,6 @@ import mage.watchers.Watcher; import java.util.*; /** - * * @author LevelX2 */ public final class SavageSummoning extends CardImpl { @@ -157,7 +156,7 @@ class SavageSummoningWatcher extends Watcher { String creatureCardKey = card.getId().toString() + '_' + (card.getZoneChangeCounter(game)); // add one because card is now gone to battlefield as creature String cardKey = cardId.toString() + '_' + zoneChangeCounter; - Set savageSpells = cardsCastWithSavageSummoning.get(creatureCardKey); + Set savageSpells = cardsCastWithSavageSummoning.get(creatureCardKey); return savageSpells != null && savageSpells.contains(cardKey); } @@ -202,11 +201,6 @@ class SavageSummoningCantCounterEffect extends ContinuousRuleModifyingEffectImpl return new SavageSummoningCantCounterEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject sourceObject = game.getObject(source); @@ -216,13 +210,16 @@ class SavageSummoningCantCounterEffect extends ContinuousRuleModifyingEffectImpl return null; } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COUNTER; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.COUNTER) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && watcher.isSpellCastWithThisSavageSummoning(spell.getId(), source.getSourceId(), zoneChangeCounter)) { - return true; - } + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null && watcher.isSpellCastWithThisSavageSummoning(spell.getId(), source.getSourceId(), zoneChangeCounter)) { + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/s/ScalelordReckoner.java b/Mage.Sets/src/mage/cards/s/ScalelordReckoner.java index 5766da6cd85..3ff98c16e20 100644 --- a/Mage.Sets/src/mage/cards/s/ScalelordReckoner.java +++ b/Mage.Sets/src/mage/cards/s/ScalelordReckoner.java @@ -1,25 +1,24 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; 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.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import java.util.UUID; + /** * * @author spjspj @@ -37,7 +36,7 @@ public final class ScalelordReckoner extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, destroy target nonland permanent that player controls. - this.addAbility(new ScalelardReckonerTriggeredAbility(new DestroyTargetEffect())); + this.addAbility(new ScalelordReckonerTriggeredAbility()); } private ScalelordReckoner(final ScalelordReckoner card) { @@ -50,49 +49,38 @@ public final class ScalelordReckoner extends CardImpl { } } -class ScalelardReckonerTriggeredAbility extends TriggeredAbilityImpl { +class ScalelordReckonerTriggeredAbility extends BecomesTargetAnyTriggeredAbility { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Dragon creature you control"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("a Dragon you control"); static { filter.add(SubType.DRAGON.getPredicate()); } - public ScalelardReckonerTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, new DestroyTargetEffect(), false); + ScalelordReckonerTriggeredAbility() { + super(new DestroyTargetEffect().setText("destroy target nonland permanent that player controls"), + filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.NONE, false); } - private ScalelardReckonerTriggeredAbility(final ScalelardReckonerTriggeredAbility ability) { + private ScalelordReckonerTriggeredAbility(final ScalelordReckonerTriggeredAbility ability) { super(ability); } @Override - public ScalelardReckonerTriggeredAbility copy() { - return new ScalelardReckonerTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; + public ScalelordReckonerTriggeredAbility copy() { + return new ScalelordReckonerTriggeredAbility(this); } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature != null && filter.match(creature, getControllerId(), this, game)) { - FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland permanent that player controls"); - filter.add(new ControllerIdPredicate(event.getPlayerId())); - this.getTargets().clear(); - this.addTarget(new TargetPermanent(filter)); - return true; - } + if (!super.checkTrigger(event, game)) { + return false; } - return false; + FilterNonlandPermanent targetFilter = new FilterNonlandPermanent("nonland permanent that player controls"); + targetFilter.add(new ControllerIdPredicate(event.getPlayerId())); + this.getTargets().clear(); + this.addTarget(new TargetPermanent(targetFilter)); + return true; } - @Override - public String getRule() { - return "Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, destroy target nonland permanent that player controls."; - } } diff --git a/Mage.Sets/src/mage/cards/s/SchemingFence.java b/Mage.Sets/src/mage/cards/s/SchemingFence.java index dfae2a6ec38..4e911fe9701 100644 --- a/Mage.Sets/src/mage/cards/s/SchemingFence.java +++ b/Mage.Sets/src/mage/cards/s/SchemingFence.java @@ -118,11 +118,6 @@ class SchemingFenceDisableEffect extends ContinuousRuleModifyingEffectImpl { return new SchemingFenceDisableEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; diff --git a/Mage.Sets/src/mage/cards/s/ScribNibblers.java b/Mage.Sets/src/mage/cards/s/ScribNibblers.java index fc917f643aa..cf1475f3b10 100644 --- a/Mage.Sets/src/mage/cards/s/ScribNibblers.java +++ b/Mage.Sets/src/mage/cards/s/ScribNibblers.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -54,7 +53,7 @@ public final class ScribNibblers extends CardImpl { class ScribNibblersEffect extends OneShotEffect { - public ScribNibblersEffect() { + ScribNibblersEffect() { super(Outcome.Neutral); this.staticText = "Exile the top card of target player's library. If it's a land card, you gain 1 life"; } @@ -72,14 +71,17 @@ class ScribNibblersEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player you = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (targetPlayer != null && targetPlayer.getLibrary().hasCards()) { - Card card = targetPlayer.getLibrary().getFromTop(game); - card.moveToExile(id, "Scrib Nibblers Exile", source, game); - if (card.isLand(game) && you != null) { - you.gainLife(1, game, source); - return true; - } + if (you == null || targetPlayer == null || !targetPlayer.getLibrary().hasCards()) { + return false; } - return false; + Card card = targetPlayer.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + you.moveCards(card, Zone.EXILED, source, game); + if (card.isLand(game)) { + you.gainLife(1, game, source); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/s/Seance.java b/Mage.Sets/src/mage/cards/s/Seance.java index c0dc61aa8f0..7a793114b4a 100644 --- a/Mage.Sets/src/mage/cards/s/Seance.java +++ b/Mage.Sets/src/mage/cards/s/Seance.java @@ -72,7 +72,7 @@ class SeanceEffect extends OneShotEffect { controller.moveCards(card, Zone.EXILED, source, game); // Also if the move to exile is replaced, the copy takes place CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, false); effect.setTargetPointer(new FixedTarget(card, game)); - effect.setAdditionalSubType(SubType.SPIRIT); + effect.withAdditionalSubType(SubType.SPIRIT); effect.apply(game, source); ExileTargetEffect exileEffect = new ExileTargetEffect(); exileEffect.setTargetPointer(new FixedTargets(effect.getAddedPermanents(), game)); diff --git a/Mage.Sets/src/mage/cards/s/SegmentedWurm.java b/Mage.Sets/src/mage/cards/s/SegmentedWurm.java index aefd03ae1e3..a93ecb91070 100644 --- a/Mage.Sets/src/mage/cards/s/SegmentedWurm.java +++ b/Mage.Sets/src/mage/cards/s/SegmentedWurm.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class SegmentedWurm extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(5); - this.addAbility(new SourceBecomesTargetTriggeredAbility(new AddCountersSourceEffect(CounterType.M1M1.createInstance()))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new AddCountersSourceEffect(CounterType.M1M1.createInstance()))); } private SegmentedWurm(final SegmentedWurm card) { diff --git a/Mage.Sets/src/mage/cards/s/Seizures.java b/Mage.Sets/src/mage/cards/s/Seizures.java index a3ef4737962..14abf0c6946 100644 --- a/Mage.Sets/src/mage/cards/s/Seizures.java +++ b/Mage.Sets/src/mage/cards/s/Seizures.java @@ -19,7 +19,6 @@ import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; -import mage.constants.Zone; /** * @author LoneFox @@ -68,12 +67,7 @@ class SeizuresEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/s/SenTriplets.java b/Mage.Sets/src/mage/cards/s/SenTriplets.java index c4f5af23d0e..f1933082f22 100644 --- a/Mage.Sets/src/mage/cards/s/SenTriplets.java +++ b/Mage.Sets/src/mage/cards/s/SenTriplets.java @@ -71,11 +71,6 @@ class SenTripletsRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl { return new SenTripletsRuleModifyingEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); diff --git a/Mage.Sets/src/mage/cards/s/SerraAvenger.java b/Mage.Sets/src/mage/cards/s/SerraAvenger.java index a3bf9f4a201..11f43b1f3a1 100644 --- a/Mage.Sets/src/mage/cards/s/SerraAvenger.java +++ b/Mage.Sets/src/mage/cards/s/SerraAvenger.java @@ -65,19 +65,19 @@ class CantCastSerraAvengerEffect extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public CantCastSerraAvengerEffect copy() { return new CantCastSerraAvengerEffect(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 (event.getType() == GameEvent.EventType.CAST_SPELL && event.getSourceId().equals(source.getSourceId())) { + if (event.getSourceId().equals(source.getSourceId())) { Player controller = game.getPlayer(source.getControllerId()); // it can be cast on other players turn 1 - 3 if some effect let allow you to do this if (controller != null && controller.getTurns() <= 3 && game.isActivePlayer(source.getControllerId())) { diff --git a/Mage.Sets/src/mage/cards/s/SerraBestiary.java b/Mage.Sets/src/mage/cards/s/SerraBestiary.java index 96082bd5866..e6c550a7736 100644 --- a/Mage.Sets/src/mage/cards/s/SerraBestiary.java +++ b/Mage.Sets/src/mage/cards/s/SerraBestiary.java @@ -80,11 +80,6 @@ class SerraBestiaryRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl return new SerraBestiaryRuleModifyingEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; @@ -92,12 +87,7 @@ class SerraBestiaryRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl @Override public boolean applies(GameEvent event, Ability source, Game game) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/s/SerraInquisitors.java b/Mage.Sets/src/mage/cards/s/SerraInquisitors.java index aec07d592a1..aa45ef3a312 100644 --- a/Mage.Sets/src/mage/cards/s/SerraInquisitors.java +++ b/Mage.Sets/src/mage/cards/s/SerraInquisitors.java @@ -36,7 +36,7 @@ public final class SerraInquisitors extends CardImpl { // Whenever Serra Inquisitors blocks or becomes blocked by one or more black creatures, Serra Inquisitors gets +2/+0 until end of turn. this.addAbility(new BlocksOrBecomesBlockedByOneOrMoreTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn), - filter, false).setReplaceRuleText(false)); + filter, false).withRuleTextReplacement(false)); } private SerraInquisitors(final SerraInquisitors card) { diff --git a/Mage.Sets/src/mage/cards/s/ShadowOfDoubt.java b/Mage.Sets/src/mage/cards/s/ShadowOfDoubt.java index 813e87295bc..320ae899ba6 100644 --- a/Mage.Sets/src/mage/cards/s/ShadowOfDoubt.java +++ b/Mage.Sets/src/mage/cards/s/ShadowOfDoubt.java @@ -57,12 +57,12 @@ class LibrariesCantBeSearchedEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - return true; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SEARCH_LIBRARY; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - return event.getType() == GameEvent.EventType.SEARCH_LIBRARY; + return true; } } diff --git a/Mage.Sets/src/mage/cards/s/ShadowProphecy.java b/Mage.Sets/src/mage/cards/s/ShadowProphecy.java index ae21a5e73c8..36b5c91dac6 100644 --- a/Mage.Sets/src/mage/cards/s/ShadowProphecy.java +++ b/Mage.Sets/src/mage/cards/s/ShadowProphecy.java @@ -22,7 +22,7 @@ public final class ShadowProphecy extends CardImpl { // Domain - Look at the top X cards of your library, where X is the number of basic land types among lands you control. Put up to two of them into your hand and the rest into your graveyard. You lose 2 life. this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect( - DomainValue.REGULAR, 2, PutCards.HAND, PutCards.GRAVEYARD + DomainValue.REGULAR, 2, PutCards.HAND, PutCards.GRAVEYARD, true )); this.getSpellAbility().addEffect(new LoseLifeSourceControllerEffect(2)); this.getSpellAbility().setAbilityWord(AbilityWord.DOMAIN); diff --git a/Mage.Sets/src/mage/cards/s/ShadowPuppeteers.java b/Mage.Sets/src/mage/cards/s/ShadowPuppeteers.java new file mode 100644 index 00000000000..61cc82b38cd --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShadowPuppeteers.java @@ -0,0 +1,117 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.FaerieRogueToken; + +import java.util.UUID; + +/** + * + * @author Susucr + */ +public final class ShadowPuppeteers extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature you control with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + } + + public ShadowPuppeteers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{U}"); + + this.subtype.add(SubType.FAERIE); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Ward {2} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"), false)); + + // When Shadow Puppeteers enters the battlefield, create two 1/1 black Faerie Rogue creature tokens with flying. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new FaerieRogueToken(), 2))); + + // Whenever a creature you control with flying attacks, you may have it become a red Dragon with base power and toughness 4/4 in addition to its other colors and types until end of turn. + this.addAbility(new AttacksCreatureYouControlTriggeredAbility( + Zone.BATTLEFIELD, new ShadowPuppeteersContinousEffect(), + true, filter, true + )); + } + + private ShadowPuppeteers(final ShadowPuppeteers card) { + super(card); + } + + @Override + public ShadowPuppeteers copy() { + return new ShadowPuppeteers(this); + } +} + +class ShadowPuppeteersContinousEffect extends ContinuousEffectImpl { + + ShadowPuppeteersContinousEffect() { + super(Duration.EndOfTurn, Outcome.Benefit); + staticText = "have it become a red Dragon with base power and toughness 4/4 " + + "in addition to its other colors and types until end of turn"; + } + + private ShadowPuppeteersContinousEffect(final ShadowPuppeteersContinousEffect effect) { + super(effect); + } + + @Override + public ShadowPuppeteersContinousEffect copy() { + return new ShadowPuppeteersContinousEffect(this); + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.PTChangingEffects_7 + || layer == Layer.ColorChangingEffects_5 + || layer == Layer.TypeChangingEffects_4; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); + if (permanent == null) { + return false; + } + switch (layer) { + case TypeChangingEffects_4: + permanent.addSubType(game, SubType.DRAGON); + break; + case ColorChangingEffects_5: + permanent.getColor(game).setRed(true); + break; + case PTChangingEffects_7: + permanent.getToughness().setModifiedBaseValue(4); + permanent.getPower().setModifiedBaseValue(4); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShannaSisaysLegacy.java b/Mage.Sets/src/mage/cards/s/ShannaSisaysLegacy.java index ef193b1a918..b078322d3c9 100644 --- a/Mage.Sets/src/mage/cards/s/ShannaSisaysLegacy.java +++ b/Mage.Sets/src/mage/cards/s/ShannaSisaysLegacy.java @@ -77,11 +77,6 @@ class ShannaSisaysLegacyEffect extends ContinuousRuleModifyingEffectImpl { return new ShannaSisaysLegacyEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/s/ShapersSanctuary.java b/Mage.Sets/src/mage/cards/s/ShapersSanctuary.java index 3a6e44ca46b..858b69eaf18 100644 --- a/Mage.Sets/src/mage/cards/s/ShapersSanctuary.java +++ b/Mage.Sets/src/mage/cards/s/ShapersSanctuary.java @@ -1,22 +1,17 @@ - package mage.cards.s; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; 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.Permanent; -import mage.players.Player; +import mage.constants.SetTargetPointer; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * - * @author TheElk801 + * @author xenohedron */ public final class ShapersSanctuary extends CardImpl { @@ -24,7 +19,9 @@ public final class ShapersSanctuary extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); // Whenever a creature you control becomes the target of a spell or ability an opponent controls, you may draw a card. - this.addAbility(new CreaturesYouControlTargetedTriggeredAbility(new DrawCardSourceControllerEffect(1))); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_CONTROLLED_A_CREATURE, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, + SetTargetPointer.NONE, true)); } private ShapersSanctuary(final ShapersSanctuary card) { @@ -36,42 +33,3 @@ public final class ShapersSanctuary extends CardImpl { return new ShapersSanctuary(this); } } - -class CreaturesYouControlTargetedTriggeredAbility extends TriggeredAbilityImpl { - - public CreaturesYouControlTargetedTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect, true); - } - - private CreaturesYouControlTargetedTriggeredAbility(final CreaturesYouControlTargetedTriggeredAbility ability) { - super(ability); - } - - @Override - public CreaturesYouControlTargetedTriggeredAbility copy() { - return new CreaturesYouControlTargetedTriggeredAbility(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()); - Player targetter = game.getPlayer(event.getPlayerId()); - if (permanent != null && permanent.isControlledBy(this.getControllerId()) && permanent.isCreature(game)) { - Object object = game.getObject(event.getSourceId()); - if (object != null && targetter != null && targetter.hasOpponent(this.getControllerId(), game)) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature 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/s/ShieldMare.java b/Mage.Sets/src/mage/cards/s/ShieldMare.java index 393fad27de2..b7dfe5055ee 100644 --- a/Mage.Sets/src/mage/cards/s/ShieldMare.java +++ b/Mage.Sets/src/mage/cards/s/ShieldMare.java @@ -2,20 +2,21 @@ package mage.cards.s; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; +import mage.abilities.meta.OrTriggeredAbility; 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 mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; import java.util.UUID; @@ -43,7 +44,10 @@ public final class ShieldMare extends CardImpl { )); // When Shield Mare enters the battlefield or becomes the target of a spell or ability and opponent controls, you gain 3 life. - this.addAbility(new ShieldMareTriggeredAbility()); + this.addAbility(new OrTriggeredAbility(Zone.ALL, new GainLifeEffect(3), false, + "When {this} enters the battlefield or becomes the target of a spell or ability an opponent controls, ", + new EntersBattlefieldTriggeredAbility(null), + new BecomesTargetSourceTriggeredAbility(null, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS))); } private ShieldMare(final ShieldMare card) { @@ -55,45 +59,3 @@ public final class ShieldMare extends CardImpl { return new ShieldMare(this); } } - -class ShieldMareTriggeredAbility extends TriggeredAbilityImpl { - - public ShieldMareTriggeredAbility() { - super(Zone.ALL, new GainLifeEffect(3)); - } - - private ShieldMareTriggeredAbility(final ShieldMareTriggeredAbility effect) { - super(effect); - } - - @Override - public ShieldMareTriggeredAbility copy() { - return new ShieldMareTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD - || event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - switch (event.getType()) { - case ENTERS_THE_BATTLEFIELD: - return event.getTargetId().equals(getSourceId()); - case TARGETED: - break; - default: - return false; - } - return event.getTargetId().equals(this.getSourceId()) - && game.getOpponents(this.getControllerId()).contains(game.getControllerId(event.getSourceId())); - } - - @Override - public String getRule() { - return "When {this} enters the battlefield or becomes the target " - + "of a spell or ability an opponent controls, you gain 3 life"; - } -} diff --git a/Mage.Sets/src/mage/cards/s/ShimmeringGlasskite.java b/Mage.Sets/src/mage/cards/s/ShimmeringGlasskite.java index c2e8b828d92..c32f0be66df 100644 --- a/Mage.Sets/src/mage/cards/s/ShimmeringGlasskite.java +++ b/Mage.Sets/src/mage/cards/s/ShimmeringGlasskite.java @@ -1,24 +1,21 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetSourceFirstTimeTriggeredAbility; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.keyword.FlyingAbility; 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.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.TargetStackObject; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * - * @author LevelX2 + * @author LevelX2, xenohedron */ public final class ShimmeringGlasskite extends CardImpl { @@ -26,7 +23,6 @@ public final class ShimmeringGlasskite extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); this.subtype.add(SubType.SPIRIT); - this.power = new MageInt(2); this.toughness = new MageInt(3); @@ -34,7 +30,10 @@ public final class ShimmeringGlasskite extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Shimmering Glasskite becomes the target of a spell or ability for the first time each turn, counter that spell or ability. - this.addAbility(new ShimmeringGlasskiteAbility()); + this.addAbility(new BecomesTargetSourceFirstTimeTriggeredAbility( + new CounterTargetEffect().setText("counter that spell or ability"), + StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.SPELL, false + )); } @@ -47,46 +46,3 @@ public final class ShimmeringGlasskite extends CardImpl { return new ShimmeringGlasskite(this); } } - -class ShimmeringGlasskiteAbility extends TriggeredAbilityImpl { - - protected int turnUsed; - - public ShimmeringGlasskiteAbility() { - super(Zone.BATTLEFIELD, new CounterTargetEffect(), false); - } - - private ShimmeringGlasskiteAbility(final ShimmeringGlasskiteAbility ability) { - super(ability); - turnUsed = ability.turnUsed; - } - - @Override - public ShimmeringGlasskiteAbility copy() { - return new ShimmeringGlasskiteAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getSourceId()) && game.getTurnNum() > turnUsed) { - this.getTargets().clear(); - TargetStackObject target = new TargetStackObject(); - target.add(event.getSourceId(), game); - this.addTarget(target); - turnUsed = game.getTurnNum(); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} becomes the target of a spell or ability for the first time each turn, counter that spell or ability."; - } - -} diff --git a/Mage.Sets/src/mage/cards/s/Silence.java b/Mage.Sets/src/mage/cards/s/Silence.java index 4956e4f5826..29244c46891 100644 --- a/Mage.Sets/src/mage/cards/s/Silence.java +++ b/Mage.Sets/src/mage/cards/s/Silence.java @@ -53,11 +53,6 @@ class SilenceEffect extends ContinuousRuleModifyingEffectImpl { return new SilenceEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/s/SilkNet.java b/Mage.Sets/src/mage/cards/s/SilkNet.java index 427fd9cdeab..9d51b87793a 100644 --- a/Mage.Sets/src/mage/cards/s/SilkNet.java +++ b/Mage.Sets/src/mage/cards/s/SilkNet.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -22,8 +21,10 @@ public final class SilkNet extends CardImpl { // Target creature gets +1/+1 and gains reach until end of turn. - this.getSpellAbility().addEffect(new BoostTargetEffect(1, 1, Duration.EndOfTurn)); - this.getSpellAbility().addEffect(new GainAbilityTargetEffect(ReachAbility.getInstance(), Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 1, Duration.EndOfTurn) + .setText("target creature gets +1/+1")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(ReachAbility.getInstance(), Duration.EndOfTurn) + .setText("and gains reach until end of turn")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/s/SilverfurPartisan.java b/Mage.Sets/src/mage/cards/s/SilverfurPartisan.java index 572f31e3cb0..2858c80c79b 100644 --- a/Mage.Sets/src/mage/cards/s/SilverfurPartisan.java +++ b/Mage.Sets/src/mage/cards/s/SilverfurPartisan.java @@ -1,30 +1,35 @@ package mage.cards.s; import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.TrampleAbility; 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.permanent.Permanent; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; import mage.game.permanent.token.WolfToken; -import mage.game.stack.StackObject; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * @author fireshoes + * @author xenohedron */ public final class SilverfurPartisan extends CardImpl { + private static final FilterPermanent filter = new FilterControlledPermanent("a Wolf or Werewolf you control"); + + static { + filter.add(Predicates.or( + SubType.WOLF.getPredicate(), + SubType.WEREWOLF.getPredicate() + )); + } + public SilverfurPartisan(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); this.subtype.add(SubType.WOLF); @@ -36,7 +41,8 @@ public final class SilverfurPartisan extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever a Wolf or Werewolf you control becomes the target of an instant or sorcery spell, create a 2/2 green Wolf creature token. - this.addAbility(new CreaturesYouControlBecomesTargetTriggeredAbility(new CreateTokenEffect(new WolfToken()))); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new CreateTokenEffect(new WolfToken()), + filter, StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY)); } private SilverfurPartisan(final SilverfurPartisan card) { @@ -48,54 +54,3 @@ public final class SilverfurPartisan extends CardImpl { return new SilverfurPartisan(this); } } - -class CreaturesYouControlBecomesTargetTriggeredAbility extends TriggeredAbilityImpl { - - public CreaturesYouControlBecomesTargetTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private CreaturesYouControlBecomesTargetTriggeredAbility(final CreaturesYouControlBecomesTargetTriggeredAbility ability) { - super(ability); - } - - @Override - public CreaturesYouControlBecomesTargetTriggeredAbility copy() { - return new CreaturesYouControlBecomesTargetTriggeredAbility(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.getPermanent(event.getTargetId()); - MageObject object = game.getObject(event.getSourceId()); - if (permanent != null - && object != null - && permanent.isControlledBy(this.controllerId) - && (permanent.hasSubtype(SubType.WOLF, game) - || permanent.hasSubtype(SubType.WEREWOLF, game))) { - if (object instanceof StackObject) { - if (object.isInstant(game) - || object.isSorcery(game)) { - if (getTargets().isEmpty()) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); - } - } - return true; - } - } - } - - return false; - } - - @Override - public String getRule() { - return "Whenever a Wolf or Werewolf you control becomes the target of an instant or sorcery spell, create a 2/2 green Wolf creature token."; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SingleCombat.java b/Mage.Sets/src/mage/cards/s/SingleCombat.java index dcc4447385f..d11ba4c4e78 100644 --- a/Mage.Sets/src/mage/cards/s/SingleCombat.java +++ b/Mage.Sets/src/mage/cards/s/SingleCombat.java @@ -109,11 +109,6 @@ class SingleCombatRestrictionEffect extends ContinuousRuleModifyingEffectImpl { return new SingleCombatRestrictionEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage.Sets/src/mage/cards/s/SkulkingFugitive.java b/Mage.Sets/src/mage/cards/s/SkulkingFugitive.java index a73b894c18e..27803bb8e1a 100644 --- a/Mage.Sets/src/mage/cards/s/SkulkingFugitive.java +++ b/Mage.Sets/src/mage/cards/s/SkulkingFugitive.java @@ -4,7 +4,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,7 +26,7 @@ public final class SkulkingFugitive extends CardImpl { this.toughness = new MageInt(4); // When Skulking Fugitive becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private SkulkingFugitive(final SkulkingFugitive card) { diff --git a/Mage.Sets/src/mage/cards/s/SkulkingGhost.java b/Mage.Sets/src/mage/cards/s/SkulkingGhost.java index aace840275c..8bbc780db9a 100644 --- a/Mage.Sets/src/mage/cards/s/SkulkingGhost.java +++ b/Mage.Sets/src/mage/cards/s/SkulkingGhost.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -26,7 +26,7 @@ public final class SkulkingGhost extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Skulking Ghost becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private SkulkingGhost(final SkulkingGhost card) { diff --git a/Mage.Sets/src/mage/cards/s/SkulkingKnight.java b/Mage.Sets/src/mage/cards/s/SkulkingKnight.java index 01255bb9e96..6b4871f8829 100644 --- a/Mage.Sets/src/mage/cards/s/SkulkingKnight.java +++ b/Mage.Sets/src/mage/cards/s/SkulkingKnight.java @@ -3,7 +3,7 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlankingAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class SkulkingKnight extends CardImpl { // Flanking this.addAbility(new FlankingAbility()); // When Skulking Knight becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private SkulkingKnight(final SkulkingKnight card) { diff --git a/Mage.Sets/src/mage/cards/s/Solfatara.java b/Mage.Sets/src/mage/cards/s/Solfatara.java index e37963c57ea..067e5aee695 100644 --- a/Mage.Sets/src/mage/cards/s/Solfatara.java +++ b/Mage.Sets/src/mage/cards/s/Solfatara.java @@ -61,11 +61,6 @@ class SolfataraEffect extends ContinuousRuleModifyingEffectImpl { return new SolfataraEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -76,11 +71,12 @@ class SolfataraEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.PLAY_LAND && event.getPlayerId().equals(source.getFirstTarget())) { - return true; - } - return false; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.PLAY_LAND; } + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getPlayerId().equals(source.getFirstTarget()); + } } diff --git a/Mage.Sets/src/mage/cards/s/SorcerousSpyglass.java b/Mage.Sets/src/mage/cards/s/SorcerousSpyglass.java index 1484a802989..0ffa5a539f4 100644 --- a/Mage.Sets/src/mage/cards/s/SorcerousSpyglass.java +++ b/Mage.Sets/src/mage/cards/s/SorcerousSpyglass.java @@ -56,11 +56,6 @@ class SorcerousSpyglassActivationEffect extends ContinuousRuleModifyingEffectImp return new SorcerousSpyglassActivationEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; diff --git a/Mage.Sets/src/mage/cards/s/SoulSeparator.java b/Mage.Sets/src/mage/cards/s/SoulSeparator.java index 2f63d6d64a4..55f7e505746 100644 --- a/Mage.Sets/src/mage/cards/s/SoulSeparator.java +++ b/Mage.Sets/src/mage/cards/s/SoulSeparator.java @@ -41,7 +41,7 @@ public final class SoulSeparator extends CardImpl { // “create” those tokens. CreateTokenCopyTargetEffect copyEffect = new CreateTokenCopyTargetEffect(null, null, false, 1, false, false, null, 1, 1, true); - copyEffect.setAdditionalSubType(SubType.SPIRIT); + copyEffect.withAdditionalSubType(SubType.SPIRIT); copyEffect.setText("Exile target creature card from your graveyard. Create a token that's a copy of that card, except it's 1/1, it's a Spirit in addition to its other types, and it has flying"); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, copyEffect, new ManaCostsImpl<>("{5}")); ability.addCost(new TapSourceCost()); diff --git a/Mage.Sets/src/mage/cards/s/SpectralPrison.java b/Mage.Sets/src/mage/cards/s/SpectralPrison.java index 75d89842814..2268f6ac827 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralPrison.java +++ b/Mage.Sets/src/mage/cards/s/SpectralPrison.java @@ -9,10 +9,7 @@ import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.EnchantAbility; 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.constants.*; import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -37,7 +34,7 @@ public final class SpectralPrison extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepEnchantedEffect())); // When enchanted creature becomes the target of a spell, sacrifice Spectral Prison. - this.addAbility(new BecomesTargetAttachedTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A)); + this.addAbility(new BecomesTargetAttachedTriggeredAbility(new SacrificeSourceEffect(), StaticFilters.FILTER_SPELL_A, SetTargetPointer.NONE, false)); } private SpectralPrison(final SpectralPrison card) { diff --git a/Mage.Sets/src/mage/cards/s/SphinxsDecree.java b/Mage.Sets/src/mage/cards/s/SphinxsDecree.java index 90ac455d72d..de6adac7454 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxsDecree.java +++ b/Mage.Sets/src/mage/cards/s/SphinxsDecree.java @@ -86,11 +86,6 @@ class SphinxsDecreeCantCastEffect extends ContinuousRuleModifyingEffectImpl { return new SphinxsDecreeCantCastEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -100,6 +95,11 @@ class SphinxsDecreeCantCastEffect extends ContinuousRuleModifyingEffectImpl { return null; } + @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) { UUID opponentId = getTargetPointer().getFirst(game, source); diff --git a/Mage.Sets/src/mage/cards/s/SpiritOfTheLabyrinth.java b/Mage.Sets/src/mage/cards/s/SpiritOfTheLabyrinth.java index 7ac7116cd35..3c67b66fed3 100644 --- a/Mage.Sets/src/mage/cards/s/SpiritOfTheLabyrinth.java +++ b/Mage.Sets/src/mage/cards/s/SpiritOfTheLabyrinth.java @@ -90,11 +90,6 @@ class SpiritOfTheLabyrinthEffect extends ContinuousRuleModifyingEffectImpl { return new SpiritOfTheLabyrinthEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DRAW_CARD; diff --git a/Mage.Sets/src/mage/cards/s/SquanderedResources.java b/Mage.Sets/src/mage/cards/s/SquanderedResources.java index b05dac40125..6b943c3f885 100644 --- a/Mage.Sets/src/mage/cards/s/SquanderedResources.java +++ b/Mage.Sets/src/mage/cards/s/SquanderedResources.java @@ -15,6 +15,7 @@ import mage.cards.CardSetInfo; import mage.choices.Choice; import mage.constants.CardType; import mage.constants.ManaType; +import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledLandPermanent; @@ -100,7 +101,7 @@ class SquanderedResourcesEffect extends ManaEffect { if (choice.getChoices().size() == 1) { choice.setChoice(choice.getChoices().iterator().next()); } else { - if (!player.choose(outcome, choice, game)) { + if (!player.choose(Outcome.PutManaInPool, choice, game)) { return mana; } } diff --git a/Mage.Sets/src/mage/cards/s/Stabilizer.java b/Mage.Sets/src/mage/cards/s/Stabilizer.java index 7865900e80b..861d41d61e8 100644 --- a/Mage.Sets/src/mage/cards/s/Stabilizer.java +++ b/Mage.Sets/src/mage/cards/s/Stabilizer.java @@ -53,11 +53,6 @@ class StabilizerEffect extends ContinuousRuleModifyingEffectImpl { return new StabilizerEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -67,11 +62,13 @@ class StabilizerEffect extends ContinuousRuleModifyingEffectImpl { return "You can't cycle cards (" + mageObject.getIdName() + ")."; } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() != GameEvent.EventType.ACTIVATE_ABILITY) { - return false; - } Ability ability = game.getAbility(event.getTargetId(), event.getSourceId()).orElse(null); return ability instanceof CyclingAbility; } diff --git a/Mage.Sets/src/mage/cards/s/StormchaserDrake.java b/Mage.Sets/src/mage/cards/s/StormchaserDrake.java index b5189a54619..c4c67739090 100644 --- a/Mage.Sets/src/mage/cards/s/StormchaserDrake.java +++ b/Mage.Sets/src/mage/cards/s/StormchaserDrake.java @@ -1,7 +1,7 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -35,7 +35,7 @@ public final class StormchaserDrake extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Stormchaser Drake becomes the target of a spell you control, draw a card. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new DrawCardSourceControllerEffect(1), filter ).setTriggerPhrase("Whenever {this} becomes the target of a spell you control, ")); } diff --git a/Mage.Sets/src/mage/cards/s/Stranglehold.java b/Mage.Sets/src/mage/cards/s/Stranglehold.java index befeb0c85fa..f80e2a16d0a 100644 --- a/Mage.Sets/src/mage/cards/s/Stranglehold.java +++ b/Mage.Sets/src/mage/cards/s/Stranglehold.java @@ -60,11 +60,6 @@ class OpponentsCantSearchLibarariesEffect extends ContinuousRuleModifyingEffectI return new OpponentsCantSearchLibarariesEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/s/SufferThePast.java b/Mage.Sets/src/mage/cards/s/SufferThePast.java index bfa68ee26ba..b16b04ffc43 100644 --- a/Mage.Sets/src/mage/cards/s/SufferThePast.java +++ b/Mage.Sets/src/mage/cards/s/SufferThePast.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.List; @@ -10,6 +9,7 @@ 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; @@ -73,7 +73,7 @@ class SufferThePastEffect extends OneShotEffect { for (UUID targetId : targets) { Card card = game.getCard(targetId); if (card != null) { - card.moveToExile(id, "Suffer the Past", source, game); + you.moveCards(card, Zone.EXILED, source, game); numberExiled ++; } } @@ -87,5 +87,3 @@ class SufferThePastEffect extends OneShotEffect { return false; } } - - diff --git a/Mage.Sets/src/mage/cards/s/SwarmShambler.java b/Mage.Sets/src/mage/cards/s/SwarmShambler.java index d54acb9165d..3f998b5bf48 100644 --- a/Mage.Sets/src/mage/cards/s/SwarmShambler.java +++ b/Mage.Sets/src/mage/cards/s/SwarmShambler.java @@ -2,7 +2,7 @@ package mage.cards.s; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -13,14 +13,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; +import mage.constants.TargetController; import mage.counters.CounterType; +import mage.filter.FilterSpell; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.game.permanent.token.InsectToken; -import mage.game.stack.Spell; import java.util.UUID; @@ -29,6 +26,11 @@ import java.util.UUID; */ public final class SwarmShambler extends CardImpl { + private static final FilterSpell filter = new FilterSpell("a spell an opponent controls"); + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + public SwarmShambler(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); @@ -44,7 +46,8 @@ public final class SwarmShambler extends CardImpl { )); // Whenever a creature you control with a +1/+1 counter on it becomes the target of a spell an opponent controls, create a 1/1 green Insect creature token. - this.addAbility(new SwarmShamblerTriggeredAbility()); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new CreateTokenEffect(new InsectToken()), + StaticFilters.FILTER_A_CONTROLLED_CREATURE_P1P1, filter)); // {1}, {T}: Put a +1/+1 counter on Swarm Shambler. Ability ability = new SimpleActivatedAbility( @@ -63,40 +66,3 @@ public final class SwarmShambler extends CardImpl { return new SwarmShambler(this); } } - -class SwarmShamblerTriggeredAbility extends TriggeredAbilityImpl { - - SwarmShamblerTriggeredAbility() { - super(Zone.BATTLEFIELD, new CreateTokenEffect(new InsectToken()), false); - } - - private SwarmShamblerTriggeredAbility(final SwarmShamblerTriggeredAbility ability) { - super(ability); - } - - @Override - public SwarmShamblerTriggeredAbility copy() { - return new SwarmShamblerTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Spell sourceObject = game.getSpell(event.getSourceId()); - Permanent permanent = game.getPermanent(event.getTargetId()); - return sourceObject != null - && permanent != null - && StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1.match(permanent, getControllerId(), this, game) - && StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS.match(sourceObject, getControllerId(), this, game); - } - - @Override - public String getRule() { - return "Whenever a creature you control with a +1/+1 counter on it " + - "becomes the target of a spell an opponent controls, create a 1/1 green Insect creature token."; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SyndicateEnforcer.java b/Mage.Sets/src/mage/cards/s/SyndicateEnforcer.java index 8b6b1575054..16c0979f8cb 100644 --- a/Mage.Sets/src/mage/cards/s/SyndicateEnforcer.java +++ b/Mage.Sets/src/mage/cards/s/SyndicateEnforcer.java @@ -23,7 +23,7 @@ public final class SyndicateEnforcer extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); - // Extort (Whenever you cast a spell, you pay {WB}. If you do, each opponent loses 1 life and you gain that much life.) + // Extort (Whenever you cast a spell, you may pay {WB}. If you do, each opponent loses 1 life and you gain that much life.) this.addAbility(new ExtortAbility()); } diff --git a/Mage.Sets/src/mage/cards/t/TamiyosCompleation.java b/Mage.Sets/src/mage/cards/t/TamiyosCompleation.java index 15e829a5971..617cc065062 100644 --- a/Mage.Sets/src/mage/cards/t/TamiyosCompleation.java +++ b/Mage.Sets/src/mage/cards/t/TamiyosCompleation.java @@ -83,7 +83,7 @@ class TamiyosCompleationTapEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent enchantment = source.getSourcePermanentIfItStillExists(game); + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment != null) { Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); if (enchanted != null) { diff --git a/Mage.Sets/src/mage/cards/t/TangleKelp.java b/Mage.Sets/src/mage/cards/t/TangleKelp.java index 68532b20d80..bb57da022ce 100644 --- a/Mage.Sets/src/mage/cards/t/TangleKelp.java +++ b/Mage.Sets/src/mage/cards/t/TangleKelp.java @@ -72,11 +72,6 @@ class DontUntapIfAttackedLastTurnEnchantedEffect extends ContinuousRuleModifying super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public DontUntapIfAttackedLastTurnEnchantedEffect copy() { return new DontUntapIfAttackedLastTurnEnchantedEffect(this); diff --git a/Mage.Sets/src/mage/cards/t/TarPitWarrior.java b/Mage.Sets/src/mage/cards/t/TarPitWarrior.java index becc6416ce1..0db257477bf 100644 --- a/Mage.Sets/src/mage/cards/t/TarPitWarrior.java +++ b/Mage.Sets/src/mage/cards/t/TarPitWarrior.java @@ -3,7 +3,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -24,7 +24,7 @@ public final class TarPitWarrior extends CardImpl { this.toughness = new MageInt(4); // When Tar Pit Warrior becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect())); } private TarPitWarrior(final TarPitWarrior card) { diff --git a/Mage.Sets/src/mage/cards/t/TaskForce.java b/Mage.Sets/src/mage/cards/t/TaskForce.java index 694ee37a856..cf0ad997355 100644 --- a/Mage.Sets/src/mage/cards/t/TaskForce.java +++ b/Mage.Sets/src/mage/cards/t/TaskForce.java @@ -1,7 +1,7 @@ package mage.cards.t; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +25,7 @@ public final class TaskForce extends CardImpl { this.toughness = new MageInt(3); // Whenever Task Force becomes the target of a spell or ability, it gets +0/+3 until end of turn. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new BoostSourceEffect(0, 3, Duration.EndOfTurn, "it") ).setTriggerPhrase("Whenever {this} becomes the target of a spell or ability, ")); } diff --git a/Mage.Sets/src/mage/cards/t/TazriStalwartSurvivor.java b/Mage.Sets/src/mage/cards/t/TazriStalwartSurvivor.java index ef780670d2a..d8d609690c6 100644 --- a/Mage.Sets/src/mage/cards/t/TazriStalwartSurvivor.java +++ b/Mage.Sets/src/mage/cards/t/TazriStalwartSurvivor.java @@ -219,7 +219,7 @@ class TazriStalwartSurvivorManaEffect extends ManaEffect { if (choice.getChoices().size() == 1) { choice.setChoice(choice.getChoices().iterator().next()); } else { - controller.choose(outcome, choice, game); + controller.choose(Outcome.PutManaInPool, choice, game); } if (choice.getChoice() == null) { return new Mana(); diff --git a/Mage.Sets/src/mage/cards/t/TectonicGiant.java b/Mage.Sets/src/mage/cards/t/TectonicGiant.java index f468bc1d025..3b521f8dc3e 100644 --- a/Mage.Sets/src/mage/cards/t/TectonicGiant.java +++ b/Mage.Sets/src/mage/cards/t/TectonicGiant.java @@ -1,31 +1,38 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.meta.OrTriggeredAbility; import mage.cards.*; import mage.constants.*; +import mage.filter.FilterSpell; import mage.filter.StaticFilters; 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.common.TargetCardInExile; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** * @author TheElk801 */ public final class TectonicGiant extends CardImpl { + private static final FilterSpell filter = new FilterSpell("a spell an opponent controls"); + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + public TectonicGiant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); @@ -37,7 +44,12 @@ public final class TectonicGiant extends CardImpl { // Whenever Tectonic Giant attacks or becomes the target of a spell an opponent controls, choose one — // • Tectonic Giant deals 3 damage to each opponent. // • Exile the top two cards of your library. Choose one of them. Until the end of your next turn, you may play that card. - this.addAbility(new TectonicGiantTriggeredAbility()); + TriggeredAbility ability = new OrTriggeredAbility(Zone.BATTLEFIELD, new DamagePlayersEffect(3, TargetController.OPPONENT), false, + "Whenever {this} attacks or becomes the target of a spell an opponent controls, ", + new AttacksTriggeredAbility(null), + new BecomesTargetSourceTriggeredAbility(null, filter)); + ability.addMode(new Mode(new TectonicGiantEffect())); + this.addAbility(ability); } private TectonicGiant(final TectonicGiant card) { @@ -50,48 +62,6 @@ public final class TectonicGiant extends CardImpl { } } -class TectonicGiantTriggeredAbility extends TriggeredAbilityImpl { - - TectonicGiantTriggeredAbility() { - super(Zone.BATTLEFIELD, new DamagePlayersEffect(3, TargetController.OPPONENT), false); - this.addMode(new Mode(new TectonicGiantEffect())); - setTriggerPhrase("Whenever {this} attacks or becomes the target of a spell an opponent controls, "); - } - - private TectonicGiantTriggeredAbility(final TectonicGiantTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS - || event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - switch (event.getType()) { - case DECLARED_ATTACKERS: - return game.getCombat().getAttackers().contains(this.getSourceId()); - case TARGETED: - if (event.getTargetId().equals(getSourceId())) { - MageObject mageObject = game.getObject(event.getSourceId()); - Player player = game.getPlayer(getControllerId()); - return mageObject != null - && mageObject instanceof Spell - && player != null - && player.hasOpponent(((Spell) mageObject).getControllerId(), game); - } - } - return false; - } - - @Override - public TectonicGiantTriggeredAbility copy() { - return new TectonicGiantTriggeredAbility(this); - } -} - class TectonicGiantEffect extends OneShotEffect { TectonicGiantEffect() { diff --git a/Mage.Sets/src/mage/cards/t/TeferiMageOfZhalfir.java b/Mage.Sets/src/mage/cards/t/TeferiMageOfZhalfir.java index 604f2522aba..76f4cfba2f2 100644 --- a/Mage.Sets/src/mage/cards/t/TeferiMageOfZhalfir.java +++ b/Mage.Sets/src/mage/cards/t/TeferiMageOfZhalfir.java @@ -144,11 +144,6 @@ class TeferiMageOfZhalfirReplacementEffect extends ContinuousRuleModifyingEffect return false; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public TeferiMageOfZhalfirReplacementEffect copy() { return new TeferiMageOfZhalfirReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java b/Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java index 7cc102bb822..e6496140593 100644 --- a/Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java +++ b/Mage.Sets/src/mage/cards/t/TeferiTimeRaveler.java @@ -104,11 +104,6 @@ class TeferiTimeRavelerReplacementEffect extends ContinuousRuleModifyingEffectIm return false; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public TeferiTimeRavelerReplacementEffect copy() { return new TeferiTimeRavelerReplacementEffect(this); diff --git a/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java b/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java index 450fa48ad17..b1bfa19eafe 100644 --- a/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java +++ b/Mage.Sets/src/mage/cards/t/TeferiTimelessVoyager.java @@ -127,11 +127,6 @@ class TeferiTimelessVoyagerPhaseEffect extends ContinuousRuleModifyingEffectImpl return event.getType() == GameEvent.EventType.PHASE_IN; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean applies(GameEvent event, Ability source, Game game) { return this.mor.refersTo(event.getTargetId(), game); diff --git a/Mage.Sets/src/mage/cards/t/TerritorialDispute.java b/Mage.Sets/src/mage/cards/t/TerritorialDispute.java index 9ab7e3ebcb2..088859697c9 100644 --- a/Mage.Sets/src/mage/cards/t/TerritorialDispute.java +++ b/Mage.Sets/src/mage/cards/t/TerritorialDispute.java @@ -64,11 +64,6 @@ class TerritorialDisputeEffect extends ContinuousRuleModifyingEffectImpl { public TerritorialDisputeEffect copy() { return new TerritorialDisputeEffect(this); } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } @Override public boolean checksEventType(GameEvent event, Game game) { diff --git a/Mage.Sets/src/mage/cards/t/TetheredSkirge.java b/Mage.Sets/src/mage/cards/t/TetheredSkirge.java index 87e9896d470..7eb2151f278 100644 --- a/Mage.Sets/src/mage/cards/t/TetheredSkirge.java +++ b/Mage.Sets/src/mage/cards/t/TetheredSkirge.java @@ -3,7 +3,7 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -28,7 +28,7 @@ public final class TetheredSkirge extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever Tethered Skirge becomes the target of a spell or ability, you lose 1 life. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new LoseLifeSourceControllerEffect(1))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new LoseLifeSourceControllerEffect(1))); } private TetheredSkirge(final TetheredSkirge card) { diff --git a/Mage.Sets/src/mage/cards/t/TetsuoUmezawa.java b/Mage.Sets/src/mage/cards/t/TetsuoUmezawa.java index 80be09b292c..39c8c711a70 100644 --- a/Mage.Sets/src/mage/cards/t/TetsuoUmezawa.java +++ b/Mage.Sets/src/mage/cards/t/TetsuoUmezawa.java @@ -86,11 +86,6 @@ class TetsuoUmezawaEffect extends ContinuousRuleModifyingEffectImpl { return event.getType() == GameEvent.EventType.TARGET; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/t/TheApprenticesFolly.java b/Mage.Sets/src/mage/cards/t/TheApprenticesFolly.java index cffad08ddd1..a7fb14d4256 100644 --- a/Mage.Sets/src/mage/cards/t/TheApprenticesFolly.java +++ b/Mage.Sets/src/mage/cards/t/TheApprenticesFolly.java @@ -10,7 +10,6 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SagaChapter; import mage.constants.SubType; -import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.ObjectSourcePlayer; @@ -49,7 +48,7 @@ public final class TheApprenticesFolly extends CardImpl { sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, new CreateTokenCopyTargetEffect(null, null, true, 1) .setIsntLegendary(true) - .setAdditionalSubType(SubType.REFLECTION) + .withAdditionalSubType(SubType.REFLECTION) .setText("choose target nontoken creature you control that doesn't have the same name as a " + "token you control. Create a token that's a copy of it, except it isn't legendary, " + "is a Reflection in addition to its other types, and has haste"), diff --git a/Mage.Sets/src/mage/cards/t/TheBookOfExaltedDeeds.java b/Mage.Sets/src/mage/cards/t/TheBookOfExaltedDeeds.java index 788d629df0c..dba00425e69 100644 --- a/Mage.Sets/src/mage/cards/t/TheBookOfExaltedDeeds.java +++ b/Mage.Sets/src/mage/cards/t/TheBookOfExaltedDeeds.java @@ -92,8 +92,9 @@ class TheBookOfExaltedDeedsEffect extends ContinuousRuleModifyingEffectImpl { } @Override - public boolean apply(Game game, Ability source) { - return true; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.WINS + || event.getType() == GameEvent.EventType.LOSES; } @Override diff --git a/Mage.Sets/src/mage/cards/t/TheCheeseStandsAlone.java b/Mage.Sets/src/mage/cards/t/TheCheeseStandsAlone.java index 1ffe60bdb28..f19c245855d 100644 --- a/Mage.Sets/src/mage/cards/t/TheCheeseStandsAlone.java +++ b/Mage.Sets/src/mage/cards/t/TheCheeseStandsAlone.java @@ -61,7 +61,8 @@ class CheeseStandsAloneContinuousEffect extends ContinuousRuleModifyingEffectImp } @Override - public boolean apply(Game game, Ability source) { + public boolean checksEventType(GameEvent event, Game game) { + // TODO: workaround to check win conditional on any event, but must be state base action instead return true; } diff --git a/Mage.Sets/src/mage/cards/t/TheHowlingAbomination.java b/Mage.Sets/src/mage/cards/t/TheHowlingAbomination.java index 248a7d1e92e..a1c35af6ac6 100644 --- a/Mage.Sets/src/mage/cards/t/TheHowlingAbomination.java +++ b/Mage.Sets/src/mage/cards/t/TheHowlingAbomination.java @@ -2,7 +2,7 @@ package mage.cards.t; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -45,7 +45,7 @@ public final class TheHowlingAbomination extends CardImpl { ))); // Electric Thunder—Whenever Blanka becomes the target of a spell, he gets +2/+2 until end of turn and deals 2 damage to each opponent. - Ability ability = new SourceBecomesTargetTriggeredAbility(new BoostSourceEffect( + Ability ability = new BecomesTargetSourceTriggeredAbility(new BoostSourceEffect( 2, 2, Duration.EndOfTurn ).setText("it gets +2/+2 until end of turn"), StaticFilters.FILTER_SPELL_A).setTriggerPhrase("Whenever {this} becomes the target of a spell, "); ability.addEffect(new DamagePlayersEffect(2, TargetController.OPPONENT) diff --git a/Mage.Sets/src/mage/cards/t/TheImmortalSun.java b/Mage.Sets/src/mage/cards/t/TheImmortalSun.java index 5932fb7d45a..036005d6b04 100644 --- a/Mage.Sets/src/mage/cards/t/TheImmortalSun.java +++ b/Mage.Sets/src/mage/cards/t/TheImmortalSun.java @@ -70,11 +70,6 @@ class TheImmortalSunCantActivateEffect extends ContinuousRuleModifyingEffectImpl return new TheImmortalSunCantActivateEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -84,17 +79,20 @@ class TheImmortalSunCantActivateEffect extends ContinuousRuleModifyingEffectImpl return null; } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.ACTIVATE_ABILITY) { - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (permanent == null) { - return false; - } - if (permanent.isPlaneswalker(game)) { - Optional ability = game.getAbility(event.getTargetId(), event.getSourceId()); - return ability.isPresent() && (ability.get() instanceof LoyaltyAbility); - } + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (permanent == null) { + return false; + } + if (permanent.isPlaneswalker(game)) { + Optional ability = game.getAbility(event.getTargetId(), event.getSourceId()); + return ability.isPresent() && (ability.get() instanceof LoyaltyAbility); } return false; } diff --git a/Mage.Sets/src/mage/cards/t/ThePhasingOfZhalfir.java b/Mage.Sets/src/mage/cards/t/ThePhasingOfZhalfir.java index bba81b5c8a1..45c81111255 100644 --- a/Mage.Sets/src/mage/cards/t/ThePhasingOfZhalfir.java +++ b/Mage.Sets/src/mage/cards/t/ThePhasingOfZhalfir.java @@ -89,11 +89,6 @@ class ThePhasingOfZhalfirPhaseEffect extends ContinuousRuleModifyingEffectImpl { return event.getType() == GameEvent.EventType.PHASE_IN; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean applies(GameEvent event, Ability source, Game game) { return event.getTargetId().equals(this.getTargetPointer().getFirst(game, source)); diff --git a/Mage.Sets/src/mage/cards/t/ThornLieutenant.java b/Mage.Sets/src/mage/cards/t/ThornLieutenant.java index 3e194e7b8b0..502440dd620 100644 --- a/Mage.Sets/src/mage/cards/t/ThornLieutenant.java +++ b/Mage.Sets/src/mage/cards/t/ThornLieutenant.java @@ -1,7 +1,7 @@ package mage.cards.t; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateTokenEffect; @@ -30,7 +30,7 @@ public final class ThornLieutenant extends CardImpl { this.toughness = new MageInt(3); // Whenever Thorn Lieutenant becomes the target of a spell or ability an opponent controls, create a 1/1 green Elf Warrior creature token. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new CreateTokenEffect(new ElfWarriorToken()), StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS ).setTriggerPhrase("Whenever {this} becomes the target of a spell or ability an opponent controls, ")); diff --git a/Mage.Sets/src/mage/cards/t/ThroneOfEldraine.java b/Mage.Sets/src/mage/cards/t/ThroneOfEldraine.java new file mode 100644 index 00000000000..8c991ad97e2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThroneOfEldraine.java @@ -0,0 +1,217 @@ +package mage.cards.t; + +import mage.ConditionalMana; +import mage.Mana; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.CostAdjuster; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.effects.common.ChooseColorEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.mana.ManaEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class ThroneOfEldraine extends CardImpl { + + public ThroneOfEldraine(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); + + this.supertype.add(SuperType.LEGENDARY); + + // As Throne of Eldraine enters the battlefield, choose a color. + this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral))); + + // {T}: Add four mana of the chosen color. Spend this mana only to cast monocolored spells of that color. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, new ThroneOfEldraineManaEffect(), new TapSourceCost())); + + // {3}, {T}: Draw two cards. Spend only mana of the chosen color to activate this ability. + Ability ability = new ConditionalActivatedAbility( + Zone.BATTLEFIELD, + new DrawCardSourceControllerEffect(2), + new GenericManaCost(3), + ThroneOfEldraineChosenColorCondition.instance, // disable the ability if no color was chosen or cost is actually {0}. + "{3}, {T}: Draw two cards. Spend only mana of the chosen color to activate this ability." + ); + ability.addCost(new TapSourceCost()); + ability.setCostAdjuster(ThroneOfEldraineAdjuster.instance); + this.addAbility(ability); + } + + private ThroneOfEldraine(final ThroneOfEldraine card) { + super(card); + } + + @Override + public ThroneOfEldraine copy() { + return new ThroneOfEldraine(this); + } +} + +class ThroneOfEldraineManaEffect extends ManaEffect { + + private static final ThroneOfEldraineManaBuilder buildWhite = new ThroneOfEldraineManaBuilder(ThroneOfEldraineCondition.white); + private static final ThroneOfEldraineManaBuilder buildBlue = new ThroneOfEldraineManaBuilder(ThroneOfEldraineCondition.blue); + private static final ThroneOfEldraineManaBuilder buildBlack = new ThroneOfEldraineManaBuilder(ThroneOfEldraineCondition.black); + private static final ThroneOfEldraineManaBuilder buildRed = new ThroneOfEldraineManaBuilder(ThroneOfEldraineCondition.red); + private static final ThroneOfEldraineManaBuilder buildGreen = new ThroneOfEldraineManaBuilder(ThroneOfEldraineCondition.green); + + private ObjectColor chosenColorInfo = null; + + ThroneOfEldraineManaEffect() { + super(); + staticText = "Add four mana of the chosen color. Spend this mana only to cast monocolored spells of that color"; + } + + private ThroneOfEldraineManaEffect(final ThroneOfEldraineManaEffect effect) { + super(effect); + chosenColorInfo = effect.chosenColorInfo; + } + + @Override + public ThroneOfEldraineManaEffect copy() { + return new ThroneOfEldraineManaEffect(this); + } + + @Override + public Mana produceMana(Game game, Ability source) { + if (game != null) { + ObjectColor color = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color"); + if (color != null) { + this.chosenColorInfo = color; + if (color.isWhite()) { + return buildWhite.setMana(Mana.WhiteMana(4), source, game).build(); + } else if (color.isBlue()) { + return buildBlue.setMana(Mana.BlueMana(4), source, game).build(); + } else if (color.isBlack()) { + return buildBlack.setMana(Mana.BlackMana(4), source, game).build(); + } else if (color.isRed()) { + return buildRed.setMana(Mana.RedMana(4), source, game).build(); + } else if (color.isGreen()) { + return buildGreen.setMana(Mana.GreenMana(4), source, game).build(); + } + } + } + return new Mana(); + } +} + +class ThroneOfEldraineManaBuilder extends ConditionalManaBuilder { + + private final ThroneOfEldraineCondition condition; + + ThroneOfEldraineManaBuilder(ThroneOfEldraineCondition condition) { + this.condition = condition; + } + + @Override + public ConditionalMana build(Object... options) { + return new ThroneOfEldraineConditionalMana(this.mana, this.condition); + } + + @Override + public String getRule() { + return "Spend this mana only to cast " + condition.toString(); + } + +} + +class ThroneOfEldraineConditionalMana extends ConditionalMana { + ThroneOfEldraineConditionalMana(Mana mana, ThroneOfEldraineCondition condition) { + super(mana); + staticText = "Spend this mana only to cast " + condition.toString(); + addCondition(condition); + } +} + +enum ThroneOfEldraineCondition implements Condition { + white(ObjectColor.WHITE, "monocolored white spells"), + blue(ObjectColor.BLUE, "monocolored blue spells"), + black(ObjectColor.BLACK, "monocolored black spells"), + red(ObjectColor.RED, "monocolored red spells"), + green(ObjectColor.GREEN, "monocolored green spells"); + + private final ObjectColor color; + private final String text; + + ThroneOfEldraineCondition(ObjectColor color, String text) { + this.color = color; + this.text = text; + } + + @Override + public boolean apply(Game game, Ability source) { + return source instanceof SpellAbility + && this.color.equals(((SpellAbility) source).getCharacteristics(game).getColor(game)); + } + + @Override + public String toString() { + return this.text; + } +} + +enum ThroneOfEldraineAdjuster implements CostAdjuster { + instance; + + @Override + public void adjustCosts(Ability ability, Game game) { + ObjectColor color = (ObjectColor) game.getState().getValue(ability.getSourceId() + "_color"); + if (color == null) { + return; + } + + int amountMana = ability.getManaCostsToPay().getMana().count(); + if (color.isWhite()) { + ability.clearManaCostsToPay(); + for (int i = 0; i < amountMana; ++i) { + ability.addManaCostsToPay(new ColoredManaCost(ColoredManaSymbol.W)); + } + } else if (color.isBlue()) { + ability.clearManaCostsToPay(); + for (int i = 0; i < amountMana; ++i) { + ability.addManaCostsToPay(new ColoredManaCost(ColoredManaSymbol.U)); + } + } else if (color.isBlack()) { + ability.clearManaCostsToPay(); + for (int i = 0; i < amountMana; ++i) { + ability.addManaCostsToPay(new ColoredManaCost(ColoredManaSymbol.B)); + } + } else if (color.isGreen()) { + ability.clearManaCostsToPay(); + for (int i = 0; i < amountMana; ++i) { + ability.addManaCostsToPay(new ColoredManaCost(ColoredManaSymbol.G)); + } + } else if (color.isRed()) { + ability.clearManaCostsToPay(); + for (int i = 0; i < amountMana; ++i) { + ability.addManaCostsToPay(new ColoredManaCost(ColoredManaSymbol.R)); + } + } + } +} + +enum ThroneOfEldraineChosenColorCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + ObjectColor color = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color"); + return color != null || source.getManaCostsToPay().getMana().count() == 0; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/ThrunBreakerOfSilence.java b/Mage.Sets/src/mage/cards/t/ThrunBreakerOfSilence.java index 38555a26c7d..612df87a086 100644 --- a/Mage.Sets/src/mage/cards/t/ThrunBreakerOfSilence.java +++ b/Mage.Sets/src/mage/cards/t/ThrunBreakerOfSilence.java @@ -77,11 +77,6 @@ class ThrunBreakerOfSilenceEffect extends ContinuousRuleModifyingEffectImpl { return new ThrunBreakerOfSilenceEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/t/ThunderbreakRegent.java b/Mage.Sets/src/mage/cards/t/ThunderbreakRegent.java index c5e4f80ffaa..23707ccf010 100644 --- a/Mage.Sets/src/mage/cards/t/ThunderbreakRegent.java +++ b/Mage.Sets/src/mage/cards/t/ThunderbreakRegent.java @@ -1,29 +1,29 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.FlyingAbility; 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.common.FilterControlledCreaturePermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; /** - * - * @author LevelX2 + * @author xenohedron */ public final class ThunderbreakRegent extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledPermanent("a Dragon you control"); + static { + filter.add(SubType.DRAGON.getPredicate()); + } + public ThunderbreakRegent(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); this.subtype.add(SubType.DRAGON); @@ -34,7 +34,8 @@ public final class ThunderbreakRegent extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever a Dragon you control becomes the target of a spell or ability your opponent controls, Thunderbreak Regent deals 3 damage to that player. - this.addAbility(new ThunderbreakRegentTriggeredAbility(new DamageTargetEffect(3))); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new DamageTargetEffect(3).setText("{this} deals 3 damage to that player"), + filter, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.PLAYER, false)); } private ThunderbreakRegent(final ThunderbreakRegent card) { @@ -46,49 +47,3 @@ public final class ThunderbreakRegent extends CardImpl { return new ThunderbreakRegent(this); } } - -class ThunderbreakRegentTriggeredAbility extends TriggeredAbilityImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Sliver creature you control"); - - static { - filter.add(SubType.DRAGON.getPredicate()); - } - - public ThunderbreakRegentTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private ThunderbreakRegentTriggeredAbility(final ThunderbreakRegentTriggeredAbility ability) { - super(ability); - } - - @Override - public ThunderbreakRegentTriggeredAbility copy() { - return new ThunderbreakRegentTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) { - Permanent creature = game.getPermanent(event.getTargetId()); - if (creature != null && filter.match(creature, getControllerId(), this, game)) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getPlayerId())); - } - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, {this} deals 3 damage to that player."; - } -} diff --git a/Mage.Sets/src/mage/cards/t/TidalBarracuda.java b/Mage.Sets/src/mage/cards/t/TidalBarracuda.java index f14f6fd12d6..97ebc722f3f 100644 --- a/Mage.Sets/src/mage/cards/t/TidalBarracuda.java +++ b/Mage.Sets/src/mage/cards/t/TidalBarracuda.java @@ -66,11 +66,6 @@ class TidalBarracudaEffect extends ContinuousRuleModifyingEffectImpl { return new TidalBarracudaEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage.Sets/src/mage/cards/t/TomikDistinguishedAdvokist.java b/Mage.Sets/src/mage/cards/t/TomikDistinguishedAdvokist.java index fc43772397b..d8d0b82991e 100644 --- a/Mage.Sets/src/mage/cards/t/TomikDistinguishedAdvokist.java +++ b/Mage.Sets/src/mage/cards/t/TomikDistinguishedAdvokist.java @@ -98,11 +98,6 @@ class TomikDistinguishedAdvokistRestrictionEffect extends ContinuousRuleModifyin return new TomikDistinguishedAdvokistRestrictionEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.PLAY_LAND; diff --git a/Mage.Sets/src/mage/cards/t/TreacherousBlessing.java b/Mage.Sets/src/mage/cards/t/TreacherousBlessing.java index c065eb83421..6253c883f49 100644 --- a/Mage.Sets/src/mage/cards/t/TreacherousBlessing.java +++ b/Mage.Sets/src/mage/cards/t/TreacherousBlessing.java @@ -1,6 +1,6 @@ package mage.cards.t; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -29,7 +29,7 @@ public final class TreacherousBlessing extends CardImpl { )); // When Treacherous Blessing becomes the target of a spell or ability, sacrifice it. - this.addAbility(new SourceBecomesTargetTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"))); } private TreacherousBlessing(final TreacherousBlessing card) { diff --git a/Mage.Sets/src/mage/cards/t/TreacherousLink.java b/Mage.Sets/src/mage/cards/t/TreacherousLink.java index 0bdb77e0432..a86997c22b4 100644 --- a/Mage.Sets/src/mage/cards/t/TreacherousLink.java +++ b/Mage.Sets/src/mage/cards/t/TreacherousLink.java @@ -95,12 +95,7 @@ class TreacherousLinkEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); Player controller = game.getPlayer(source.getControllerId()); DamageEvent damageEvent = (DamageEvent) event; if (controller != null && enchantment != null) { diff --git a/Mage.Sets/src/mage/cards/t/TurfWound.java b/Mage.Sets/src/mage/cards/t/TurfWound.java index 5750e3722ac..a06bfb729d0 100644 --- a/Mage.Sets/src/mage/cards/t/TurfWound.java +++ b/Mage.Sets/src/mage/cards/t/TurfWound.java @@ -58,11 +58,6 @@ class TurfWoundEffect extends ContinuousRuleModifyingEffectImpl { return new TurfWoundEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -72,9 +67,14 @@ class TurfWoundEffect extends ContinuousRuleModifyingEffectImpl { return null; } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.PLAY_LAND; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.PLAY_LAND && event.getPlayerId().equals(source.getFirstTarget())) { + if (event.getPlayerId().equals(source.getFirstTarget())) { return true; } return false; diff --git a/Mage.Sets/src/mage/cards/u/UnsettledMariner.java b/Mage.Sets/src/mage/cards/u/UnsettledMariner.java index 36097f1a93d..7f945c7da06 100644 --- a/Mage.Sets/src/mage/cards/u/UnsettledMariner.java +++ b/Mage.Sets/src/mage/cards/u/UnsettledMariner.java @@ -1,21 +1,16 @@ package mage.cards.u; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.TargetOfOpponentsSpellOrAbilityTriggeredAbility; +import mage.abilities.common.BecomesTargetControllerTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.keyword.ChangelingAbility; 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.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; import java.util.UUID; @@ -36,7 +31,8 @@ public final class UnsettledMariner extends CardImpl { // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, // counter that spell or ability unless its controller pays {1}. - this.addAbility(new TargetOfOpponentsSpellOrAbilityTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(1)))); + this.addAbility(new BecomesTargetControllerTriggeredAbility(new CounterUnlessPaysEffect(new GenericManaCost(1)), + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, SetTargetPointer.SPELL, false)); } private UnsettledMariner(final UnsettledMariner card) { diff --git a/Mage.Sets/src/mage/cards/u/UrzaPrinceOfKroog.java b/Mage.Sets/src/mage/cards/u/UrzaPrinceOfKroog.java index 6d78c70e51d..f5935e7575a 100644 --- a/Mage.Sets/src/mage/cards/u/UrzaPrinceOfKroog.java +++ b/Mage.Sets/src/mage/cards/u/UrzaPrinceOfKroog.java @@ -42,7 +42,7 @@ public final class UrzaPrinceOfKroog extends CardImpl { Ability ability = new SimpleActivatedAbility(new CreateTokenCopyTargetEffect( null, CardType.CREATURE, false, 1, false, false, null, 1, 1, false - ).setAdditionalSubType(SubType.SOLDIER).setText("create a token that's a copy of target artifact you control, " + + ).withAdditionalSubType(SubType.SOLDIER).setText("create a token that's a copy of target artifact you control, " + "except it's a 1/1 Soldier creature in addition to its other types"), new GenericManaCost(6)); ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/v/VeilstoneAmulet.java b/Mage.Sets/src/mage/cards/v/VeilstoneAmulet.java index ac1a21578cc..598b91c86ac 100644 --- a/Mage.Sets/src/mage/cards/v/VeilstoneAmulet.java +++ b/Mage.Sets/src/mage/cards/v/VeilstoneAmulet.java @@ -56,11 +56,6 @@ class VeilstoneAmuletEffect extends ContinuousRuleModifyingEffectImpl { return new VeilstoneAmuletEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.TARGET; diff --git a/Mage.Sets/src/mage/cards/v/VeneratedRotpriest.java b/Mage.Sets/src/mage/cards/v/VeneratedRotpriest.java index 3f65f7521cf..f9ecc19d3b7 100644 --- a/Mage.Sets/src/mage/cards/v/VeneratedRotpriest.java +++ b/Mage.Sets/src/mage/cards/v/VeneratedRotpriest.java @@ -1,28 +1,22 @@ package mage.cards.v; -import java.util.UUID; - import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.common.FightTargetSourceEffect; +import mage.abilities.Ability; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; import mage.abilities.effects.common.counter.AddPoisonCounterTargetEffect; -import mage.constants.SubType; import mage.abilities.keyword.ToxicAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; -import mage.target.TargetPermanent; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * @author TheElk801 + * @author xenohedron */ public final class VeneratedRotpriest extends CardImpl { @@ -38,7 +32,10 @@ public final class VeneratedRotpriest extends CardImpl { this.addAbility(new ToxicAbility(1)); // Whenever a creature you control becomes the target of a spell, target opponent gets a poison counter. - this.addAbility(new VeneratedRotpriestTriggeredAbility()); + Ability ability = new BecomesTargetAnyTriggeredAbility(new AddPoisonCounterTargetEffect(1), + StaticFilters.FILTER_CONTROLLED_A_CREATURE, StaticFilters.FILTER_SPELL_A, SetTargetPointer.NONE, false); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); } private VeneratedRotpriest(final VeneratedRotpriest card) { @@ -50,41 +47,3 @@ public final class VeneratedRotpriest extends CardImpl { return new VeneratedRotpriest(this); } } - -class VeneratedRotpriestTriggeredAbility extends TriggeredAbilityImpl { - - VeneratedRotpriestTriggeredAbility() { - super(Zone.BATTLEFIELD, new AddPoisonCounterTargetEffect(1)); - this.addTarget(new TargetOpponent()); - } - - private VeneratedRotpriestTriggeredAbility(final VeneratedRotpriestTriggeredAbility ability) { - super(ability); - } - - @Override - public VeneratedRotpriestTriggeredAbility copy() { - return new VeneratedRotpriestTriggeredAbility(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.getPermanent(event.getTargetId()); - MageObject object = game.getObject(event.getSourceId()); - return permanent != null - && object != null - && isControlledBy(permanent.getControllerId()) - && permanent.isCreature(game) - && object instanceof Spell; - } - - @Override - public String getRule() { - return "Whenever a creature you control becomes the target of a spell, target opponent gets a poison counter."; - } -} diff --git a/Mage.Sets/src/mage/cards/v/VexingShusher.java b/Mage.Sets/src/mage/cards/v/VexingShusher.java index 4aa0c5825e1..ad916aca08f 100644 --- a/Mage.Sets/src/mage/cards/v/VexingShusher.java +++ b/Mage.Sets/src/mage/cards/v/VexingShusher.java @@ -69,11 +69,6 @@ class VexingShusherCantCounterTargetEffect extends ContinuousRuleModifyingEffect return new VexingShusherCantCounterTargetEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject sourceObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/v/VisionsOfBrutality.java b/Mage.Sets/src/mage/cards/v/VisionsOfBrutality.java index 9f49b2330e3..7dc0f3bfdf8 100644 --- a/Mage.Sets/src/mage/cards/v/VisionsOfBrutality.java +++ b/Mage.Sets/src/mage/cards/v/VisionsOfBrutality.java @@ -60,7 +60,7 @@ public final class VisionsOfBrutality extends CardImpl { class VisionsOfBrutalityEffect extends OneShotEffect { - public VisionsOfBrutalityEffect() { + VisionsOfBrutalityEffect() { super(Outcome.Benefit); this.staticText = "its controller loses that much life"; } @@ -77,28 +77,20 @@ class VisionsOfBrutalityEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } - if (enchantment != null - && enchantment.getAttachedTo() != null) { - Permanent enchanted = game.getPermanentOrLKIBattlefield(enchantment.getAttachedTo()); - if (enchanted != null) { - Player controllerEnchanted = game.getPlayer(enchanted.getControllerId()); - if (controllerEnchanted != null) { - int damage = (Integer) getValue("damage"); - if (damage > 0) { - controllerEnchanted.loseLife(damage, game, source, false); - } - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); + if (controller == null || enchantment == null || enchantment.getAttachedTo() == null) { + return false; + } + Permanent enchanted = game.getPermanentOrLKIBattlefield(enchantment.getAttachedTo()); + if (enchanted != null) { + Player controllerEnchanted = game.getPlayer(enchanted.getControllerId()); + if (controllerEnchanted != null) { + int damage = (Integer) getValue("damage"); + if (damage > 0) { + controllerEnchanted.loseLife(damage, game, source, false); } } - return true; } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/v/VoidWinnower.java b/Mage.Sets/src/mage/cards/v/VoidWinnower.java index f20caec720a..42970bde8e9 100644 --- a/Mage.Sets/src/mage/cards/v/VoidWinnower.java +++ b/Mage.Sets/src/mage/cards/v/VoidWinnower.java @@ -61,11 +61,6 @@ class VoidWinnowerCantCastEffect extends ContinuousRuleModifyingEffectImpl { return new VoidWinnowerCantCastEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/v/VoidstoneGargoyle.java b/Mage.Sets/src/mage/cards/v/VoidstoneGargoyle.java index 753a33899fb..6d82e5a5049 100644 --- a/Mage.Sets/src/mage/cards/v/VoidstoneGargoyle.java +++ b/Mage.Sets/src/mage/cards/v/VoidstoneGargoyle.java @@ -13,7 +13,6 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.util.CardUtil; import java.util.UUID; @@ -61,11 +60,6 @@ class VoidstoneGargoyleReplacementEffect1 extends ContinuousRuleModifyingEffectI super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public VoidstoneGargoyleReplacementEffect1 copy() { return new VoidstoneGargoyleReplacementEffect1(this); @@ -80,14 +74,16 @@ class VoidstoneGargoyleReplacementEffect1 extends ContinuousRuleModifyingEffectI return null; } + @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 (event.getType() == GameEvent.EventType.CAST_SPELL) { - MageObject object = game.getObject(event.getSourceId()); - String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - return CardUtil.haveSameNames(object, cardName, game); - } - return false; + MageObject object = game.getObject(event.getSourceId()); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return CardUtil.haveSameNames(object, cardName, game); } } @@ -103,11 +99,6 @@ class VoidstoneGargoyleRuleModifyingEffect2 extends ContinuousRuleModifyingEffec super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public VoidstoneGargoyleRuleModifyingEffect2 copy() { return new VoidstoneGargoyleRuleModifyingEffect2(this); @@ -123,13 +114,14 @@ class VoidstoneGargoyleRuleModifyingEffect2 extends ContinuousRuleModifyingEffec } @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == GameEvent.EventType.ACTIVATE_ABILITY) { - MageObject object = game.getObject(event.getSourceId()); - String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - return CardUtil.haveSameNames(object, cardName, game); - } - return false; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; } + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + MageObject object = game.getObject(event.getSourceId()); + String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); + return CardUtil.haveSameNames(object, cardName, game); + } } diff --git a/Mage.Sets/src/mage/cards/v/VolrathsCurse.java b/Mage.Sets/src/mage/cards/v/VolrathsCurse.java index 6e3ce68eada..35b7b3049f1 100644 --- a/Mage.Sets/src/mage/cards/v/VolrathsCurse.java +++ b/Mage.Sets/src/mage/cards/v/VolrathsCurse.java @@ -120,11 +120,6 @@ class VolrathsCurseCantActivateAbilitiesEffect extends ContinuousRuleModifyingEf return new VolrathsCurseCantActivateAbilitiesEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; diff --git a/Mage.Sets/src/mage/cards/w/WardOfBones.java b/Mage.Sets/src/mage/cards/w/WardOfBones.java index 982dd234aad..738e59ecf04 100644 --- a/Mage.Sets/src/mage/cards/w/WardOfBones.java +++ b/Mage.Sets/src/mage/cards/w/WardOfBones.java @@ -64,11 +64,6 @@ class WardOfBonesEffect extends ContinuousRuleModifyingEffectImpl { return new WardOfBonesEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java b/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java index 8597e1b07d6..6bcdf000f94 100644 --- a/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java +++ b/Mage.Sets/src/mage/cards/w/WardenOfTheWoods.java @@ -1,7 +1,7 @@ package mage.cards.w; import mage.MageInt; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; @@ -29,11 +29,10 @@ public final class WardenOfTheWoods extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Whenever Warden of the Woods becomes the target of a spell or ability an opponent controls, you may draw two cards. - this.addAbility(new SourceBecomesTargetTriggeredAbility( + this.addAbility(new BecomesTargetSourceTriggeredAbility( new DrawCardSourceControllerEffect(2), StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS, - SetTargetPointer.NONE, true - ).setTriggerPhrase("Whenever {this} becomes the target of a spell or ability an opponent controls, ")); + SetTargetPointer.NONE, true)); } private WardenOfTheWoods(final WardenOfTheWoods card) { diff --git a/Mage.Sets/src/mage/cards/w/WardscaleDragon.java b/Mage.Sets/src/mage/cards/w/WardscaleDragon.java index ff965f0ca6d..b41d887a7ea 100644 --- a/Mage.Sets/src/mage/cards/w/WardscaleDragon.java +++ b/Mage.Sets/src/mage/cards/w/WardscaleDragon.java @@ -64,11 +64,6 @@ class WardscaleDragonRuleEffect extends ContinuousRuleModifyingEffectImpl { return new WardscaleDragonRuleEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage.Sets/src/mage/cards/w/WargRider.java b/Mage.Sets/src/mage/cards/w/WargRider.java new file mode 100644 index 00000000000..9c9dfa5b664 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WargRider.java @@ -0,0 +1,71 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.keyword.AmassEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class WargRider extends CardImpl { + + private static final FilterControlledCreaturePermanent filterOrcAndGoblins = + new FilterControlledCreaturePermanent("Orcs and Goblins"); + + static { + filterOrcAndGoblins.add( + Predicates.or( + SubType.ORC.getPredicate(), + SubType.GOBLIN.getPredicate() + ) + ); + } + + public WargRider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.subtype.add(SubType.ORC); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // Other Orcs and Goblins you control have menace. + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, + new GainAbilityControlledEffect( + new MenaceAbility(false), + Duration.WhileOnBattlefield, + filterOrcAndGoblins, + true + ) + )); + + // At the beginning of combat on your turn, amass Orcs 2. + this.addAbility(new BeginningOfCombatTriggeredAbility( + new AmassEffect(2, SubType.ORC), + TargetController.YOU, false + )); + } + + private WargRider(final WargRider card) { + super(card); + } + + @Override + public WargRider copy() { + return new WargRider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/Wellwisher.java b/Mage.Sets/src/mage/cards/w/Wellwisher.java index c70ec7d283b..01a1f7c4792 100644 --- a/Mage.Sets/src/mage/cards/w/Wellwisher.java +++ b/Mage.Sets/src/mage/cards/w/Wellwisher.java @@ -7,6 +7,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -33,10 +34,10 @@ public final class Wellwisher extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - // {tap}: You gain 1 life for each Elf on the battlefield. + // {T}: You gain 1 life for each Elf on the battlefield. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainLifeEffect(new PermanentsOnBattlefieldCount(filter)) - .setText("you gain 1 life for each Elf on the battlefield"), new TapSourceCost())); - + .setText("you gain 1 life for each Elf on the battlefield"), new TapSourceCost()) + .addHint(new ValueHint("Elves on the battlefield", new PermanentsOnBattlefieldCount(filter)))); } private Wellwisher(final Wellwisher card) { diff --git a/Mage.Sets/src/mage/cards/w/WildDefiance.java b/Mage.Sets/src/mage/cards/w/WildDefiance.java index ee74a5bba37..73f3c2a3605 100644 --- a/Mage.Sets/src/mage/cards/w/WildDefiance.java +++ b/Mage.Sets/src/mage/cards/w/WildDefiance.java @@ -1,24 +1,17 @@ package mage.cards.w; -import java.util.UUID; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; 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.Duration; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; -import mage.target.targetpointer.FixedTarget; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * @author noxx + * @author xenohedron */ public final class WildDefiance extends CardImpl { @@ -26,7 +19,9 @@ public final class WildDefiance extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); // Whenever a creature you control becomes the target of an instant or sorcery spell, that creature gets +3/+3 until end of turn. - this.addAbility(new CreaturesYouControlBecomesTargetTriggeredAbility(new BoostTargetEffect(3, 3, Duration.EndOfTurn))); + this.addAbility(new BecomesTargetAnyTriggeredAbility( + new BoostTargetEffect(3, 3, Duration.EndOfTurn).setText("that creature gets +3/+3 until end of turn"), + StaticFilters.FILTER_CONTROLLED_A_CREATURE, StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY)); } private WildDefiance(final WildDefiance card) { @@ -38,51 +33,3 @@ public final class WildDefiance extends CardImpl { return new WildDefiance(this); } } - -class CreaturesYouControlBecomesTargetTriggeredAbility extends TriggeredAbilityImpl { - - public CreaturesYouControlBecomesTargetTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); - } - - private CreaturesYouControlBecomesTargetTriggeredAbility(final CreaturesYouControlBecomesTargetTriggeredAbility ability) { - super(ability); - } - - @Override - public CreaturesYouControlBecomesTargetTriggeredAbility copy() { - return new CreaturesYouControlBecomesTargetTriggeredAbility(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.getPermanent(event.getTargetId()); - if (permanent != null - && permanent.isControlledBy(this.controllerId) - && permanent.isCreature(game)) { - MageObject object = game.getObject(event.getSourceId()); - if (object instanceof Spell) { - Card c = (Spell) object; - if (c.isInstantOrSorcery(game)) { - if (getTargets().isEmpty()) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId(), game)); - } - } - return true; - } - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature you control becomes the target of an instant or sorcery spell, that creature gets +3/+3 until end of turn."; - } -} diff --git a/Mage.Sets/src/mage/cards/w/Willbreaker.java b/Mage.Sets/src/mage/cards/w/Willbreaker.java index a8fda611681..48ddbfd422f 100644 --- a/Mage.Sets/src/mage/cards/w/Willbreaker.java +++ b/Mage.Sets/src/mage/cards/w/Willbreaker.java @@ -1,26 +1,26 @@ package mage.cards.w; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; 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.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 mage.constants.*; +import mage.filter.FilterStackObject; +import mage.filter.StaticFilters; import java.util.UUID; /** - * @author LevelX2 + * @author xenohedron */ public final class Willbreaker extends CardImpl { + private static final FilterStackObject filter = new FilterStackObject("a spell or ability you control"); + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + public Willbreaker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.subtype.add(SubType.HUMAN); @@ -29,7 +29,8 @@ public final class Willbreaker extends CardImpl { this.toughness = new MageInt(3); // Whenever a creature an opponent controls becomes the target of a spell or ability you control, gain control of that creature for as long as you control Willbreaker. - this.addAbility(new WillbreakerTriggeredAbility()); + this.addAbility(new BecomesTargetAnyTriggeredAbility(new GainControlTargetEffect(Duration.WhileControlled), + StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE, filter, SetTargetPointer.PERMANENT, false)); } private Willbreaker(final Willbreaker card) { @@ -41,48 +42,3 @@ public final class Willbreaker extends CardImpl { return new Willbreaker(this); } } - -class WillbreakerTriggeredAbility extends TriggeredAbilityImpl { - - WillbreakerTriggeredAbility() { - super(Zone.BATTLEFIELD, new GainControlTargetEffect(Duration.WhileControlled)); - } - - private WillbreakerTriggeredAbility(final WillbreakerTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!isControlledBy(event.getPlayerId())) { - return false; - } - Permanent permanent = game.getPermanent(event.getTargetId()); - Permanent willbreaker = game.getPermanent(sourceId); - if (willbreaker == null // If you lose control of Willbreaker before its ability resolves, you won’t gain control of the creature at all. - || permanent == null - || !permanent.isCreature(game) - || !game.getOpponents(getControllerId()).contains(permanent.getControllerId())) { - return false; - } - // always call this method for FixedTargets in case it is blinked - this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); - return true; - } - - @Override - public String getRule() { - return "Whenever a creature an opponent controls becomes the target of a spell or ability you control, " - + "gain control of that creature for as long as you control {this}."; - } - - @Override - public WillbreakerTriggeredAbility copy() { - return new WillbreakerTriggeredAbility(this); - } -} diff --git a/Mage.Sets/src/mage/cards/w/WormfangDrake.java b/Mage.Sets/src/mage/cards/w/WormfangDrake.java index 71d1e66a8f1..64bde245c2d 100644 --- a/Mage.Sets/src/mage/cards/w/WormfangDrake.java +++ b/Mage.Sets/src/mage/cards/w/WormfangDrake.java @@ -43,7 +43,7 @@ public final class WormfangDrake extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility( new SacrificeSourceUnlessPaysEffect(new ExileTargetCost(new TargetControlledPermanent(filter))), false - ).setReplaceRuleText(false)); + ).withRuleTextReplacement(false)); // When Wormfang Drake leaves the battlefield, return the exiled card to the battlefield under its owner's control. this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false)); diff --git a/Mage.Sets/src/mage/cards/w/WormsOfTheEarth.java b/Mage.Sets/src/mage/cards/w/WormsOfTheEarth.java index 2646e0d23f6..ce2713e34ac 100644 --- a/Mage.Sets/src/mage/cards/w/WormsOfTheEarth.java +++ b/Mage.Sets/src/mage/cards/w/WormsOfTheEarth.java @@ -65,11 +65,6 @@ class WormsOfTheEarthPlayEffect extends ContinuousRuleModifyingEffectImpl { return new WormsOfTheEarthPlayEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.PLAY_LAND; diff --git a/Mage.Sets/src/mage/cards/x/XanatharGuildKingpin.java b/Mage.Sets/src/mage/cards/x/XanatharGuildKingpin.java index c62c5ece0d3..e8d822eb5c1 100644 --- a/Mage.Sets/src/mage/cards/x/XanatharGuildKingpin.java +++ b/Mage.Sets/src/mage/cards/x/XanatharGuildKingpin.java @@ -80,11 +80,6 @@ class XanatharGuildKingpinRuleModifyingEffect extends ContinuousRuleModifyingEff return new XanatharGuildKingpinRuleModifyingEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); diff --git a/Mage.Sets/src/mage/cards/x/XantidSwarm.java b/Mage.Sets/src/mage/cards/x/XantidSwarm.java index 7dda88c653f..26971ba4bee 100644 --- a/Mage.Sets/src/mage/cards/x/XantidSwarm.java +++ b/Mage.Sets/src/mage/cards/x/XantidSwarm.java @@ -99,11 +99,6 @@ class XantidSwarmReplacementEffect extends ContinuousRuleModifyingEffectImpl { return new XantidSwarmReplacementEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage.Sets/src/mage/cards/y/YasharnImplacableEarth.java b/Mage.Sets/src/mage/cards/y/YasharnImplacableEarth.java index cffb44e5198..14db564898c 100644 --- a/Mage.Sets/src/mage/cards/y/YasharnImplacableEarth.java +++ b/Mage.Sets/src/mage/cards/y/YasharnImplacableEarth.java @@ -128,11 +128,6 @@ class YasharnImplacableEarthEffect extends ContinuousRuleModifyingEffectImpl { return new YasharnImplacableEarthEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -142,14 +137,15 @@ class YasharnImplacableEarthEffect extends ContinuousRuleModifyingEffectImpl { return null; } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY + || event.getType() == GameEvent.EventType.CAST_SPELL; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (event.getType() != GameEvent.EventType.ACTIVATE_ABILITY - && event.getType() != GameEvent.EventType.CAST_SPELL) { - return false; - } - if (event.getType() == GameEvent.EventType.ACTIVATE_ABILITY && permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/y/YuanShaosInfantry.java b/Mage.Sets/src/mage/cards/y/YuanShaosInfantry.java index de105fd8c97..707d6923df4 100644 --- a/Mage.Sets/src/mage/cards/y/YuanShaosInfantry.java +++ b/Mage.Sets/src/mage/cards/y/YuanShaosInfantry.java @@ -28,7 +28,7 @@ public final class YuanShaosInfantry extends CardImpl { // Whenever Yuan Shao's Infantry attacks alone, Yuan Shao's Infantry can't be blocked this combat. Effect effect = new CantBeBlockedSourceEffect(Duration.EndOfCombat); effect.setText("{this} can't be blocked this combat"); - this.addAbility(new AttacksAloneSourceTriggeredAbility(effect).setReplaceRuleText(false)); + this.addAbility(new AttacksAloneSourceTriggeredAbility(effect).withRuleTextReplacement(false)); } private YuanShaosInfantry(final YuanShaosInfantry card) { diff --git a/Mage.Sets/src/mage/cards/z/ZhurTaaDruid.java b/Mage.Sets/src/mage/cards/z/ZhurTaaDruid.java index c06ba2dd9f8..b34a48e4217 100644 --- a/Mage.Sets/src/mage/cards/z/ZhurTaaDruid.java +++ b/Mage.Sets/src/mage/cards/z/ZhurTaaDruid.java @@ -51,6 +51,7 @@ class ZhurTaaDruidAbility extends TriggeredAbilityImpl { ZhurTaaDruidAbility() { super(Zone.BATTLEFIELD, new DamagePlayersEffect(1, TargetController.OPPONENT)); setTriggerPhrase("Whenever you tap {this} for mana, "); + this.replaceRuleText = true; } private ZhurTaaDruidAbility(final ZhurTaaDruidAbility ability) { diff --git a/Mage.Sets/src/mage/sets/AmonkhetInvocations.java b/Mage.Sets/src/mage/sets/AmonkhetInvocations.java index 511ecdf2d52..2d7be3233e2 100644 --- a/Mage.Sets/src/mage/sets/AmonkhetInvocations.java +++ b/Mage.Sets/src/mage/sets/AmonkhetInvocations.java @@ -24,59 +24,59 @@ public final class AmonkhetInvocations extends ExpansionSet { this.hasBoosters = false; this.hasBasicLands = false; - cards.add(new SetCardInfo("Aggravated Assault", 25, Rarity.MYTHIC, mage.cards.a.AggravatedAssault.class)); - cards.add(new SetCardInfo("Armageddon", 31, Rarity.MYTHIC, mage.cards.a.Armageddon.class)); - cards.add(new SetCardInfo("Attrition", 19, Rarity.MYTHIC, mage.cards.a.Attrition.class)); - cards.add(new SetCardInfo("Austere Command", 1, Rarity.MYTHIC, mage.cards.a.AustereCommand.class)); - cards.add(new SetCardInfo("Avatar of Woe", 38, Rarity.MYTHIC, mage.cards.a.AvatarOfWoe.class)); - cards.add(new SetCardInfo("Aven Mindcensor", 2, Rarity.MYTHIC, mage.cards.a.AvenMindcensor.class)); - cards.add(new SetCardInfo("Blood Moon", 46, Rarity.MYTHIC, mage.cards.b.BloodMoon.class)); - cards.add(new SetCardInfo("Boil", 47, Rarity.MYTHIC, mage.cards.b.Boil.class)); - cards.add(new SetCardInfo("Bontu the Glorified", 20, Rarity.MYTHIC, mage.cards.b.BontuTheGlorified.class)); - cards.add(new SetCardInfo("Capsize", 32, Rarity.MYTHIC, mage.cards.c.Capsize.class)); - cards.add(new SetCardInfo("Chain Lightning", 26, Rarity.MYTHIC, mage.cards.c.ChainLightning.class)); - cards.add(new SetCardInfo("Choke", 50, Rarity.MYTHIC, mage.cards.c.Choke.class)); - cards.add(new SetCardInfo("Consecrated Sphinx", 8, Rarity.MYTHIC, mage.cards.c.ConsecratedSphinx.class)); - cards.add(new SetCardInfo("Containment Priest", 3, Rarity.MYTHIC, mage.cards.c.ContainmentPriest.class)); - cards.add(new SetCardInfo("Counterbalance", 9, Rarity.MYTHIC, mage.cards.c.Counterbalance.class)); - cards.add(new SetCardInfo("Counterspell", 10, Rarity.MYTHIC, mage.cards.c.Counterspell.class)); - cards.add(new SetCardInfo("Cryptic Command", 11, Rarity.MYTHIC, mage.cards.c.CrypticCommand.class)); - cards.add(new SetCardInfo("Damnation", 39, Rarity.MYTHIC, mage.cards.d.Damnation.class)); - cards.add(new SetCardInfo("Dark Ritual", 21, Rarity.MYTHIC, mage.cards.d.DarkRitual.class)); - cards.add(new SetCardInfo("Daze", 12, Rarity.MYTHIC, mage.cards.d.Daze.class)); - cards.add(new SetCardInfo("Desolation Angel", 40, Rarity.MYTHIC, mage.cards.d.DesolationAngel.class)); - cards.add(new SetCardInfo("Diabolic Edict", 41, Rarity.MYTHIC, mage.cards.d.DiabolicEdict.class)); - cards.add(new SetCardInfo("Diabolic Intent", 22, Rarity.MYTHIC, mage.cards.d.DiabolicIntent.class)); - cards.add(new SetCardInfo("Divert", 13, Rarity.MYTHIC, mage.cards.d.Divert.class)); - cards.add(new SetCardInfo("Doomsday", 42, Rarity.MYTHIC, mage.cards.d.Doomsday.class)); - cards.add(new SetCardInfo("Entomb", 23, Rarity.MYTHIC, mage.cards.e.Entomb.class)); - cards.add(new SetCardInfo("Forbid", 33, Rarity.MYTHIC, mage.cards.f.Forbid.class)); - cards.add(new SetCardInfo("Force of Will", 14, Rarity.MYTHIC, mage.cards.f.ForceOfWill.class)); - cards.add(new SetCardInfo("Hazoret the Fervent", 27, Rarity.MYTHIC, mage.cards.h.HazoretTheFervent.class)); - cards.add(new SetCardInfo("Kefnet the Mindful", 15, Rarity.MYTHIC, mage.cards.k.KefnetTheMindful.class)); - cards.add(new SetCardInfo("Lord of Extinction", 52, Rarity.MYTHIC, mage.cards.l.LordOfExtinction.class)); - cards.add(new SetCardInfo("Loyal Retainers", 4, Rarity.MYTHIC, mage.cards.l.LoyalRetainers.class)); - cards.add(new SetCardInfo("Maelstrom Pulse", 29, Rarity.MYTHIC, mage.cards.m.MaelstromPulse.class)); - cards.add(new SetCardInfo("Mind Twist", 24, Rarity.MYTHIC, mage.cards.m.MindTwist.class)); - cards.add(new SetCardInfo("No Mercy", 43, Rarity.MYTHIC, mage.cards.n.NoMercy.class)); - cards.add(new SetCardInfo("Oketra the True", 5, Rarity.MYTHIC, mage.cards.o.OketraTheTrue.class)); - cards.add(new SetCardInfo("Omniscience", 34, Rarity.MYTHIC, mage.cards.o.Omniscience.class)); - cards.add(new SetCardInfo("Opposition", 35, Rarity.MYTHIC, mage.cards.o.Opposition.class)); - cards.add(new SetCardInfo("Pact of Negation", 16, Rarity.MYTHIC, mage.cards.p.PactOfNegation.class)); - cards.add(new SetCardInfo("Rhonas the Indomitable", 28, Rarity.MYTHIC, mage.cards.r.RhonasTheIndomitable.class)); - cards.add(new SetCardInfo("Shatterstorm", 48, Rarity.MYTHIC, mage.cards.s.Shatterstorm.class)); - cards.add(new SetCardInfo("Slaughter Pact", 44, Rarity.MYTHIC, mage.cards.s.SlaughterPact.class)); - cards.add(new SetCardInfo("Spell Pierce", 17, Rarity.MYTHIC, mage.cards.s.SpellPierce.class)); - cards.add(new SetCardInfo("Stifle", 18, Rarity.MYTHIC, mage.cards.s.Stifle.class)); - cards.add(new SetCardInfo("Sunder", 36, Rarity.MYTHIC, mage.cards.s.Sunder.class)); - cards.add(new SetCardInfo("The Locust God", 51, Rarity.MYTHIC, mage.cards.t.TheLocustGod.class)); - cards.add(new SetCardInfo("The Scarab God", 53, Rarity.MYTHIC, mage.cards.t.TheScarabGod.class)); - cards.add(new SetCardInfo("The Scorpion God", 54, Rarity.MYTHIC, mage.cards.t.TheScorpionGod.class)); - cards.add(new SetCardInfo("Thoughtseize", 45, Rarity.MYTHIC, mage.cards.t.Thoughtseize.class)); - cards.add(new SetCardInfo("Threads of Disloyalty", 37, Rarity.MYTHIC, mage.cards.t.ThreadsOfDisloyalty.class)); - cards.add(new SetCardInfo("Through the Breach", 49, Rarity.MYTHIC, mage.cards.t.ThroughTheBreach.class)); - cards.add(new SetCardInfo("Vindicate", 30, Rarity.MYTHIC, mage.cards.v.Vindicate.class)); - cards.add(new SetCardInfo("Worship", 6, Rarity.MYTHIC, mage.cards.w.Worship.class)); - cards.add(new SetCardInfo("Wrath of God", 7, Rarity.MYTHIC, mage.cards.w.WrathOfGod.class)); + cards.add(new SetCardInfo("Aggravated Assault", 25, Rarity.SPECIAL, mage.cards.a.AggravatedAssault.class)); + cards.add(new SetCardInfo("Armageddon", 31, Rarity.SPECIAL, mage.cards.a.Armageddon.class)); + cards.add(new SetCardInfo("Attrition", 19, Rarity.SPECIAL, mage.cards.a.Attrition.class)); + cards.add(new SetCardInfo("Austere Command", 1, Rarity.SPECIAL, mage.cards.a.AustereCommand.class)); + cards.add(new SetCardInfo("Avatar of Woe", 38, Rarity.SPECIAL, mage.cards.a.AvatarOfWoe.class)); + cards.add(new SetCardInfo("Aven Mindcensor", 2, Rarity.SPECIAL, mage.cards.a.AvenMindcensor.class)); + cards.add(new SetCardInfo("Blood Moon", 46, Rarity.SPECIAL, mage.cards.b.BloodMoon.class)); + cards.add(new SetCardInfo("Boil", 47, Rarity.SPECIAL, mage.cards.b.Boil.class)); + cards.add(new SetCardInfo("Bontu the Glorified", 20, Rarity.SPECIAL, mage.cards.b.BontuTheGlorified.class)); + cards.add(new SetCardInfo("Capsize", 32, Rarity.SPECIAL, mage.cards.c.Capsize.class)); + cards.add(new SetCardInfo("Chain Lightning", 26, Rarity.SPECIAL, mage.cards.c.ChainLightning.class)); + cards.add(new SetCardInfo("Choke", 50, Rarity.SPECIAL, mage.cards.c.Choke.class)); + cards.add(new SetCardInfo("Consecrated Sphinx", 8, Rarity.SPECIAL, mage.cards.c.ConsecratedSphinx.class)); + cards.add(new SetCardInfo("Containment Priest", 3, Rarity.SPECIAL, mage.cards.c.ContainmentPriest.class)); + cards.add(new SetCardInfo("Counterbalance", 9, Rarity.SPECIAL, mage.cards.c.Counterbalance.class)); + cards.add(new SetCardInfo("Counterspell", 10, Rarity.SPECIAL, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Cryptic Command", 11, Rarity.SPECIAL, mage.cards.c.CrypticCommand.class)); + cards.add(new SetCardInfo("Damnation", 39, Rarity.SPECIAL, mage.cards.d.Damnation.class)); + cards.add(new SetCardInfo("Dark Ritual", 21, Rarity.SPECIAL, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Daze", 12, Rarity.SPECIAL, mage.cards.d.Daze.class)); + cards.add(new SetCardInfo("Desolation Angel", 40, Rarity.SPECIAL, mage.cards.d.DesolationAngel.class)); + cards.add(new SetCardInfo("Diabolic Edict", 41, Rarity.SPECIAL, mage.cards.d.DiabolicEdict.class)); + cards.add(new SetCardInfo("Diabolic Intent", 22, Rarity.SPECIAL, mage.cards.d.DiabolicIntent.class)); + cards.add(new SetCardInfo("Divert", 13, Rarity.SPECIAL, mage.cards.d.Divert.class)); + cards.add(new SetCardInfo("Doomsday", 42, Rarity.SPECIAL, mage.cards.d.Doomsday.class)); + cards.add(new SetCardInfo("Entomb", 23, Rarity.SPECIAL, mage.cards.e.Entomb.class)); + cards.add(new SetCardInfo("Forbid", 33, Rarity.SPECIAL, mage.cards.f.Forbid.class)); + cards.add(new SetCardInfo("Force of Will", 14, Rarity.SPECIAL, mage.cards.f.ForceOfWill.class)); + cards.add(new SetCardInfo("Hazoret the Fervent", 27, Rarity.SPECIAL, mage.cards.h.HazoretTheFervent.class)); + cards.add(new SetCardInfo("Kefnet the Mindful", 15, Rarity.SPECIAL, mage.cards.k.KefnetTheMindful.class)); + cards.add(new SetCardInfo("Lord of Extinction", 52, Rarity.SPECIAL, mage.cards.l.LordOfExtinction.class)); + cards.add(new SetCardInfo("Loyal Retainers", 4, Rarity.SPECIAL, mage.cards.l.LoyalRetainers.class)); + cards.add(new SetCardInfo("Maelstrom Pulse", 29, Rarity.SPECIAL, mage.cards.m.MaelstromPulse.class)); + cards.add(new SetCardInfo("Mind Twist", 24, Rarity.SPECIAL, mage.cards.m.MindTwist.class)); + cards.add(new SetCardInfo("No Mercy", 43, Rarity.SPECIAL, mage.cards.n.NoMercy.class)); + cards.add(new SetCardInfo("Oketra the True", 5, Rarity.SPECIAL, mage.cards.o.OketraTheTrue.class)); + cards.add(new SetCardInfo("Omniscience", 34, Rarity.SPECIAL, mage.cards.o.Omniscience.class)); + cards.add(new SetCardInfo("Opposition", 35, Rarity.SPECIAL, mage.cards.o.Opposition.class)); + cards.add(new SetCardInfo("Pact of Negation", 16, Rarity.SPECIAL, mage.cards.p.PactOfNegation.class)); + cards.add(new SetCardInfo("Rhonas the Indomitable", 28, Rarity.SPECIAL, mage.cards.r.RhonasTheIndomitable.class)); + cards.add(new SetCardInfo("Shatterstorm", 48, Rarity.SPECIAL, mage.cards.s.Shatterstorm.class)); + cards.add(new SetCardInfo("Slaughter Pact", 44, Rarity.SPECIAL, mage.cards.s.SlaughterPact.class)); + cards.add(new SetCardInfo("Spell Pierce", 17, Rarity.SPECIAL, mage.cards.s.SpellPierce.class)); + cards.add(new SetCardInfo("Stifle", 18, Rarity.SPECIAL, mage.cards.s.Stifle.class)); + cards.add(new SetCardInfo("Sunder", 36, Rarity.SPECIAL, mage.cards.s.Sunder.class)); + cards.add(new SetCardInfo("The Locust God", 51, Rarity.SPECIAL, mage.cards.t.TheLocustGod.class)); + cards.add(new SetCardInfo("The Scarab God", 53, Rarity.SPECIAL, mage.cards.t.TheScarabGod.class)); + cards.add(new SetCardInfo("The Scorpion God", 54, Rarity.SPECIAL, mage.cards.t.TheScorpionGod.class)); + cards.add(new SetCardInfo("Thoughtseize", 45, Rarity.SPECIAL, mage.cards.t.Thoughtseize.class)); + cards.add(new SetCardInfo("Threads of Disloyalty", 37, Rarity.SPECIAL, mage.cards.t.ThreadsOfDisloyalty.class)); + cards.add(new SetCardInfo("Through the Breach", 49, Rarity.SPECIAL, mage.cards.t.ThroughTheBreach.class)); + cards.add(new SetCardInfo("Vindicate", 30, Rarity.SPECIAL, mage.cards.v.Vindicate.class)); + cards.add(new SetCardInfo("Worship", 6, Rarity.SPECIAL, mage.cards.w.Worship.class)); + cards.add(new SetCardInfo("Wrath of God", 7, Rarity.SPECIAL, mage.cards.w.WrathOfGod.class)); } } diff --git a/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java b/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java index dc0c569b159..bb77ce6f46d 100644 --- a/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java +++ b/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java @@ -313,6 +313,8 @@ public final class CommanderLegendsBattleForBaldursGate extends ExpansionSet { cards.add(new SetCardInfo("Hunted Horror", 758, Rarity.RARE, mage.cards.h.HuntedHorror.class)); cards.add(new SetCardInfo("Icewind Stalwart", 27, Rarity.COMMON, mage.cards.i.IcewindStalwart.class)); cards.add(new SetCardInfo("Ignite the Future", 797, Rarity.RARE, mage.cards.i.IgniteTheFuture.class)); + cards.add(new SetCardInfo("Illithid Harvester", 76, Rarity.RARE, mage.cards.i.IllithidHarvester.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Illithid Harvester", 564, Rarity.RARE, mage.cards.i.IllithidHarvester.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Imoen, Mystic Trickster", 77, Rarity.UNCOMMON, mage.cards.i.ImoenMysticTrickster.class)); cards.add(new SetCardInfo("In Garruk's Wake", 759, Rarity.RARE, mage.cards.i.InGarruksWake.class)); cards.add(new SetCardInfo("Ingenious Artillerist", 182, Rarity.COMMON, mage.cards.i.IngeniousArtillerist.class)); diff --git a/Mage.Sets/src/mage/sets/CustomAlchemy.java b/Mage.Sets/src/mage/sets/CustomAlchemy.java new file mode 100644 index 00000000000..58fa985a0bd --- /dev/null +++ b/Mage.Sets/src/mage/sets/CustomAlchemy.java @@ -0,0 +1,29 @@ + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * Custom version of the official cards. + * Similar to Alchemy tweaks of cards. + * @author Susucr + */ +public final class CustomAlchemy extends ExpansionSet { + + private static final CustomAlchemy instance = new CustomAlchemy(); + + public static CustomAlchemy getInstance() { + return instance; + } + + private CustomAlchemy() { + super("Custom Alchemy", "CALC", ExpansionSet.buildDate(2023, 9, 24), SetType.CUSTOM_SET); + this.hasBoosters = false; + this.hasBasicLands = false; + + cards.add(new SetCardInfo("C-Pillar of the Paruns", 1, Rarity.SPECIAL, mage.cards.p.PillarOfTheParunsCustom.class)); + } + +} diff --git a/Mage.Sets/src/mage/sets/IceAge.java b/Mage.Sets/src/mage/sets/IceAge.java index b717fde1b66..6250e2ff4fa 100644 --- a/Mage.Sets/src/mage/sets/IceAge.java +++ b/Mage.Sets/src/mage/sets/IceAge.java @@ -199,6 +199,7 @@ public final class IceAge extends ExpansionSet { cards.add(new SetCardInfo("Kjeldoran Dead", 137, Rarity.COMMON, mage.cards.k.KjeldoranDead.class)); cards.add(new SetCardInfo("Kjeldoran Elite Guard", 34, Rarity.UNCOMMON, mage.cards.k.KjeldoranEliteGuard.class)); cards.add(new SetCardInfo("Kjeldoran Frostbeast", 296, Rarity.UNCOMMON, mage.cards.k.KjeldoranFrostbeast.class)); + cards.add(new SetCardInfo("Kjeldoran Guard", 35, Rarity.COMMON, mage.cards.k.KjeldoranGuard.class)); cards.add(new SetCardInfo("Kjeldoran Knight", 36, Rarity.RARE, mage.cards.k.KjeldoranKnight.class)); cards.add(new SetCardInfo("Kjeldoran Phalanx", 37, Rarity.RARE, mage.cards.k.KjeldoranPhalanx.class)); cards.add(new SetCardInfo("Kjeldoran Royal Guard", 38, Rarity.RARE, mage.cards.k.KjeldoranRoyalGuard.class)); diff --git a/Mage.Sets/src/mage/sets/JurassicWorldCollection.java b/Mage.Sets/src/mage/sets/JurassicWorldCollection.java new file mode 100644 index 00000000000..b739374c392 --- /dev/null +++ b/Mage.Sets/src/mage/sets/JurassicWorldCollection.java @@ -0,0 +1,23 @@ + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.SetType; + +/** + * @author Susucr + */ +public final class JurassicWorldCollection extends ExpansionSet { + + private static final JurassicWorldCollection instance = new JurassicWorldCollection(); + + public static JurassicWorldCollection getInstance() { + return instance; + } + + private JurassicWorldCollection() { + super("Jurassic World Collection", "REX", ExpansionSet.buildDate(2023, 11, 17), SetType.SUPPLEMENTAL); + this.hasBoosters = false; + this.hasBasicLands = false; + } +} diff --git a/Mage.Sets/src/mage/sets/KaladeshInventions.java b/Mage.Sets/src/mage/sets/KaladeshInventions.java index e9081d98caf..b9d17f18e74 100644 --- a/Mage.Sets/src/mage/sets/KaladeshInventions.java +++ b/Mage.Sets/src/mage/sets/KaladeshInventions.java @@ -27,59 +27,59 @@ public final class KaladeshInventions extends ExpansionSet { CardGraphicInfo cardGraphicInfo = new CardGraphicInfo(FrameStyle.KLD_INVENTION, false); - cards.add(new SetCardInfo("Aether Vial", 6, Rarity.MYTHIC, mage.cards.a.AetherVial.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Arcbound Ravager", 31, Rarity.MYTHIC, mage.cards.a.ArcboundRavager.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Black Vise", 32, Rarity.MYTHIC, mage.cards.b.BlackVise.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Cataclysmic Gearhulk", 1, Rarity.MYTHIC, mage.cards.c.CataclysmicGearhulk.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Chalice of the Void", 33, Rarity.MYTHIC, mage.cards.c.ChaliceOfTheVoid.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Champion's Helm", 7, Rarity.MYTHIC, mage.cards.c.ChampionsHelm.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Chromatic Lantern", 8, Rarity.MYTHIC, mage.cards.c.ChromaticLantern.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Chrome Mox", 9, Rarity.MYTHIC, mage.cards.c.ChromeMox.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Cloudstone Curio", 10, Rarity.MYTHIC, mage.cards.c.CloudstoneCurio.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Combustible Gearhulk", 4, Rarity.MYTHIC, mage.cards.c.CombustibleGearhulk.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Crucible of Worlds", 11, Rarity.MYTHIC, mage.cards.c.CrucibleOfWorlds.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Defense Grid", 34, Rarity.MYTHIC, mage.cards.d.DefenseGrid.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Duplicant", 35, Rarity.MYTHIC, mage.cards.d.Duplicant.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Engineered Explosives", 36, Rarity.MYTHIC, mage.cards.e.EngineeredExplosives.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Ensnaring Bridge", 37, Rarity.MYTHIC, mage.cards.e.EnsnaringBridge.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Extraplanar Lens", 38, Rarity.MYTHIC, mage.cards.e.ExtraplanarLens.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Gauntlet of Power", 12, Rarity.MYTHIC, mage.cards.g.GauntletOfPower.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Grindstone", 39, Rarity.MYTHIC, mage.cards.g.Grindstone.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Hangarback Walker", 13, Rarity.MYTHIC, mage.cards.h.HangarbackWalker.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Lightning Greaves", 14, Rarity.MYTHIC, mage.cards.l.LightningGreaves.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Lotus Petal", 15, Rarity.MYTHIC, mage.cards.l.LotusPetal.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Mana Crypt", 16, Rarity.MYTHIC, mage.cards.m.ManaCrypt.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Mana Vault", 17, Rarity.MYTHIC, mage.cards.m.ManaVault.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Meekstone", 40, Rarity.MYTHIC, mage.cards.m.Meekstone.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Mind's Eye", 18, Rarity.MYTHIC, mage.cards.m.MindsEye.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Mox Opal", 19, Rarity.MYTHIC, mage.cards.m.MoxOpal.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Noxious Gearhulk", 3, Rarity.MYTHIC, mage.cards.n.NoxiousGearhulk.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Oblivion Stone", 41, Rarity.MYTHIC, mage.cards.o.OblivionStone.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Ornithopter", 42, Rarity.MYTHIC, mage.cards.o.Ornithopter.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Painter's Servant", 20, Rarity.MYTHIC, mage.cards.p.PaintersServant.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Paradox Engine", 43, Rarity.MYTHIC, mage.cards.p.ParadoxEngine.class)); - cards.add(new SetCardInfo("Pithing Needle", 44, Rarity.MYTHIC, mage.cards.p.PithingNeedle.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Planar Bridge", 45, Rarity.MYTHIC, mage.cards.p.PlanarBridge.class)); - cards.add(new SetCardInfo("Platinum Angel", 46, Rarity.MYTHIC, mage.cards.p.PlatinumAngel.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Rings of Brighthearth", 21, Rarity.MYTHIC, mage.cards.r.RingsOfBrighthearth.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Scroll Rack", 22, Rarity.MYTHIC, mage.cards.s.ScrollRack.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Sculpting Steel", 23, Rarity.MYTHIC, mage.cards.s.SculptingSteel.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Sol Ring", 24, Rarity.MYTHIC, mage.cards.s.SolRing.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Solemn Simulacrum", 25, Rarity.MYTHIC, mage.cards.s.SolemnSimulacrum.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Sphere of Resistance", 47, Rarity.MYTHIC, mage.cards.s.SphereOfResistance.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Staff of Domination", 48, Rarity.MYTHIC, mage.cards.s.StaffOfDomination.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Static Orb", 26, Rarity.MYTHIC, mage.cards.s.StaticOrb.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Steel Overseer", 27, Rarity.MYTHIC, mage.cards.s.SteelOverseer.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Sundering Titan", 49, Rarity.MYTHIC, mage.cards.s.SunderingTitan.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Sword of Body and Mind", 50, Rarity.MYTHIC, mage.cards.s.SwordOfBodyAndMind.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Sword of Feast and Famine", 28, Rarity.MYTHIC, mage.cards.s.SwordOfFeastAndFamine.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Sword of Fire and Ice", 29, Rarity.MYTHIC, mage.cards.s.SwordOfFireAndIce.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Sword of Light and Shadow", 30, Rarity.MYTHIC, mage.cards.s.SwordOfLightAndShadow.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Sword of War and Peace", 51, Rarity.MYTHIC, mage.cards.s.SwordOfWarAndPeace.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Torrential Gearhulk", 2, Rarity.MYTHIC, mage.cards.t.TorrentialGearhulk.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Trinisphere", 52, Rarity.MYTHIC, mage.cards.t.Trinisphere.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Vedalken Shackles", 53, Rarity.MYTHIC, mage.cards.v.VedalkenShackles.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Verdurous Gearhulk", 5, Rarity.MYTHIC, mage.cards.v.VerdurousGearhulk.class, cardGraphicInfo)); - cards.add(new SetCardInfo("Wurmcoil Engine", 54, Rarity.MYTHIC, mage.cards.w.WurmcoilEngine.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Aether Vial", 6, Rarity.SPECIAL, mage.cards.a.AetherVial.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Arcbound Ravager", 31, Rarity.SPECIAL, mage.cards.a.ArcboundRavager.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Black Vise", 32, Rarity.SPECIAL, mage.cards.b.BlackVise.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Cataclysmic Gearhulk", 1, Rarity.SPECIAL, mage.cards.c.CataclysmicGearhulk.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Chalice of the Void", 33, Rarity.SPECIAL, mage.cards.c.ChaliceOfTheVoid.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Champion's Helm", 7, Rarity.SPECIAL, mage.cards.c.ChampionsHelm.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Chromatic Lantern", 8, Rarity.SPECIAL, mage.cards.c.ChromaticLantern.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Chrome Mox", 9, Rarity.SPECIAL, mage.cards.c.ChromeMox.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Cloudstone Curio", 10, Rarity.SPECIAL, mage.cards.c.CloudstoneCurio.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Combustible Gearhulk", 4, Rarity.SPECIAL, mage.cards.c.CombustibleGearhulk.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Crucible of Worlds", 11, Rarity.SPECIAL, mage.cards.c.CrucibleOfWorlds.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Defense Grid", 34, Rarity.SPECIAL, mage.cards.d.DefenseGrid.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Duplicant", 35, Rarity.SPECIAL, mage.cards.d.Duplicant.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Engineered Explosives", 36, Rarity.SPECIAL, mage.cards.e.EngineeredExplosives.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Ensnaring Bridge", 37, Rarity.SPECIAL, mage.cards.e.EnsnaringBridge.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Extraplanar Lens", 38, Rarity.SPECIAL, mage.cards.e.ExtraplanarLens.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Gauntlet of Power", 12, Rarity.SPECIAL, mage.cards.g.GauntletOfPower.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Grindstone", 39, Rarity.SPECIAL, mage.cards.g.Grindstone.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Hangarback Walker", 13, Rarity.SPECIAL, mage.cards.h.HangarbackWalker.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Lightning Greaves", 14, Rarity.SPECIAL, mage.cards.l.LightningGreaves.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Lotus Petal", 15, Rarity.SPECIAL, mage.cards.l.LotusPetal.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Mana Crypt", 16, Rarity.SPECIAL, mage.cards.m.ManaCrypt.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Mana Vault", 17, Rarity.SPECIAL, mage.cards.m.ManaVault.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Meekstone", 40, Rarity.SPECIAL, mage.cards.m.Meekstone.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Mind's Eye", 18, Rarity.SPECIAL, mage.cards.m.MindsEye.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Mox Opal", 19, Rarity.SPECIAL, mage.cards.m.MoxOpal.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Noxious Gearhulk", 3, Rarity.SPECIAL, mage.cards.n.NoxiousGearhulk.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Oblivion Stone", 41, Rarity.SPECIAL, mage.cards.o.OblivionStone.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Ornithopter", 42, Rarity.SPECIAL, mage.cards.o.Ornithopter.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Painter's Servant", 20, Rarity.SPECIAL, mage.cards.p.PaintersServant.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Paradox Engine", 43, Rarity.SPECIAL, mage.cards.p.ParadoxEngine.class)); + cards.add(new SetCardInfo("Pithing Needle", 44, Rarity.SPECIAL, mage.cards.p.PithingNeedle.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Planar Bridge", 45, Rarity.SPECIAL, mage.cards.p.PlanarBridge.class)); + cards.add(new SetCardInfo("Platinum Angel", 46, Rarity.SPECIAL, mage.cards.p.PlatinumAngel.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Rings of Brighthearth", 21, Rarity.SPECIAL, mage.cards.r.RingsOfBrighthearth.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Scroll Rack", 22, Rarity.SPECIAL, mage.cards.s.ScrollRack.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Sculpting Steel", 23, Rarity.SPECIAL, mage.cards.s.SculptingSteel.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Sol Ring", 24, Rarity.SPECIAL, mage.cards.s.SolRing.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Solemn Simulacrum", 25, Rarity.SPECIAL, mage.cards.s.SolemnSimulacrum.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Sphere of Resistance", 47, Rarity.SPECIAL, mage.cards.s.SphereOfResistance.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Staff of Domination", 48, Rarity.SPECIAL, mage.cards.s.StaffOfDomination.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Static Orb", 26, Rarity.SPECIAL, mage.cards.s.StaticOrb.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Steel Overseer", 27, Rarity.SPECIAL, mage.cards.s.SteelOverseer.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Sundering Titan", 49, Rarity.SPECIAL, mage.cards.s.SunderingTitan.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Sword of Body and Mind", 50, Rarity.SPECIAL, mage.cards.s.SwordOfBodyAndMind.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Sword of Feast and Famine", 28, Rarity.SPECIAL, mage.cards.s.SwordOfFeastAndFamine.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Sword of Fire and Ice", 29, Rarity.SPECIAL, mage.cards.s.SwordOfFireAndIce.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Sword of Light and Shadow", 30, Rarity.SPECIAL, mage.cards.s.SwordOfLightAndShadow.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Sword of War and Peace", 51, Rarity.SPECIAL, mage.cards.s.SwordOfWarAndPeace.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Torrential Gearhulk", 2, Rarity.SPECIAL, mage.cards.t.TorrentialGearhulk.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Trinisphere", 52, Rarity.SPECIAL, mage.cards.t.Trinisphere.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Vedalken Shackles", 53, Rarity.SPECIAL, mage.cards.v.VedalkenShackles.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Verdurous Gearhulk", 5, Rarity.SPECIAL, mage.cards.v.VerdurousGearhulk.class, cardGraphicInfo)); + cards.add(new SetCardInfo("Wurmcoil Engine", 54, Rarity.SPECIAL, mage.cards.w.WurmcoilEngine.class, cardGraphicInfo)); } } diff --git a/Mage.Sets/src/mage/sets/LostCavernsOfIxalan.java b/Mage.Sets/src/mage/sets/LostCavernsOfIxalan.java new file mode 100644 index 00000000000..aef16fa9f04 --- /dev/null +++ b/Mage.Sets/src/mage/sets/LostCavernsOfIxalan.java @@ -0,0 +1,31 @@ + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author Susucr + */ +public final class LostCavernsOfIxalan extends ExpansionSet { + + private static final LostCavernsOfIxalan instance = new LostCavernsOfIxalan(); + + public static LostCavernsOfIxalan getInstance() { + return instance; + } + + private LostCavernsOfIxalan() { + super("Lost Caverns of Ixalan", "LCI", ExpansionSet.buildDate(2023, 11, 17), SetType.EXPANSION); + this.hasBoosters = false; // TODO: enable boosters + this.hasBasicLands = true; + + cards.add(new SetCardInfo("Cavern of Souls", 269, Rarity.MYTHIC, mage.cards.c.CavernOfSouls.class)); + cards.add(new SetCardInfo("Forest", 291, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 288, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 290, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 287, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 289, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + } +} diff --git a/Mage.Sets/src/mage/sets/LostCavernsOfIxalanCommander.java b/Mage.Sets/src/mage/sets/LostCavernsOfIxalanCommander.java new file mode 100644 index 00000000000..fc78633b789 --- /dev/null +++ b/Mage.Sets/src/mage/sets/LostCavernsOfIxalanCommander.java @@ -0,0 +1,26 @@ + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author Susucr + */ +public final class LostCavernsOfIxalanCommander extends ExpansionSet { + + private static final LostCavernsOfIxalanCommander instance = new LostCavernsOfIxalanCommander(); + + public static LostCavernsOfIxalanCommander getInstance() { + return instance; + } + + private LostCavernsOfIxalanCommander() { + super("Lost Caverns of Ixalan Commander", "LCC", ExpansionSet.buildDate(2023, 11, 17), SetType.SUPPLEMENTAL); + this.hasBoosters = false; + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Coercive Portal", 109, Rarity.MYTHIC, mage.cards.c.CoercivePortal.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/MysteryBoosterPlaytest.java b/Mage.Sets/src/mage/sets/MysteryBoosterPlaytest.java index 55c123eb519..82689b6150a 100644 --- a/Mage.Sets/src/mage/sets/MysteryBoosterPlaytest.java +++ b/Mage.Sets/src/mage/sets/MysteryBoosterPlaytest.java @@ -28,6 +28,7 @@ public class MysteryBoosterPlaytest extends ExpansionSet { cards.add(new SetCardInfo("Frogkin Kidnapper", 42, Rarity.RARE, mage.cards.f.FrogkinKidnapper.class)); cards.add(new SetCardInfo("How to Keep an Izzet Mage Busy", 93, Rarity.RARE, mage.cards.h.HowToKeepAnIzzetMageBusy.class)); cards.add(new SetCardInfo("Innocuous Insect", 23, Rarity.RARE, mage.cards.i.InnocuousInsect.class)); + cards.add(new SetCardInfo("Lazier Goblin", 56, Rarity.RARE, mage.cards.l.LazierGoblin.class)); cards.add(new SetCardInfo("Mirrored Lotus", 107, Rarity.RARE, mage.cards.m.MirroredLotus.class)); cards.add(new SetCardInfo("Recycla-bird", 28, Rarity.RARE, mage.cards.r.RecyclaBird.class)); cards.add(new SetCardInfo("Slivdrazi Monstrosity", 102, Rarity.RARE, mage.cards.s.SlivdraziMonstrosity.class)); diff --git a/Mage.Sets/src/mage/sets/SpecialGuests.java b/Mage.Sets/src/mage/sets/SpecialGuests.java new file mode 100644 index 00000000000..56caf5d7477 --- /dev/null +++ b/Mage.Sets/src/mage/sets/SpecialGuests.java @@ -0,0 +1,27 @@ + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author Susucr + */ +public final class SpecialGuests extends ExpansionSet { + + private static final SpecialGuests instance = new SpecialGuests(); + + public static SpecialGuests getInstance() { + return instance; + } + + private SpecialGuests() { + super("Special Guests", "SPG", ExpansionSet.buildDate(2023, 11, 17), SetType.SUPPLEMENTAL); + this.hasBoosters = false; + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Lord of Atlantis", 1, Rarity.RARE, mage.cards.l.LordOfAtlantis.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mana Crypt", 17, Rarity.MYTHIC, mage.cards.m.ManaCrypt.class, FULL_ART_BFZ_VARIOUS)); + } +} diff --git a/Mage.Sets/src/mage/sets/TalesOfMiddleEarthCommander.java b/Mage.Sets/src/mage/sets/TalesOfMiddleEarthCommander.java index 0c8cd35cce3..7d6347a0085 100644 --- a/Mage.Sets/src/mage/sets/TalesOfMiddleEarthCommander.java +++ b/Mage.Sets/src/mage/sets/TalesOfMiddleEarthCommander.java @@ -16,19 +16,21 @@ public final class TalesOfMiddleEarthCommander extends ExpansionSet { super("Tales of Middle-earth Commander", "LTC", ExpansionSet.buildDate(2023, 6, 23), SetType.SUPPLEMENTAL); this.hasBasicLands = false; + cards.add(new SetCardInfo("Abyssal Persecutor", 525, Rarity.MYTHIC, mage.cards.a.AbyssalPersecutor.class)); cards.add(new SetCardInfo("Access Tunnel", 294, Rarity.UNCOMMON, mage.cards.a.AccessTunnel.class)); cards.add(new SetCardInfo("Ancient Tomb", 357, Rarity.MYTHIC, mage.cards.a.AncientTomb.class)); cards.add(new SetCardInfo("Anger", 210, Rarity.UNCOMMON, mage.cards.a.Anger.class)); cards.add(new SetCardInfo("Anguished Unmaking", 265, Rarity.RARE, mage.cards.a.AnguishedUnmaking.class)); - cards.add(new SetCardInfo("Aragorn, King of Gondor", 5, Rarity.MYTHIC, mage.cards.a.AragornKingOfGondor.class)); + cards.add(new SetCardInfo("Aragorn, King of Gondor", 448, Rarity.MYTHIC, mage.cards.a.AragornKingOfGondor.class)); cards.add(new SetCardInfo("Arbor Elf", 232, Rarity.COMMON, mage.cards.a.ArborElf.class)); + cards.add(new SetCardInfo("Arboreal Alliance", 541, Rarity.RARE, mage.cards.a.ArborealAlliance.class)); cards.add(new SetCardInfo("Arcane Denial", 184, Rarity.COMMON, mage.cards.a.ArcaneDenial.class)); cards.add(new SetCardInfo("Arcane Signet", 273, Rarity.COMMON, mage.cards.a.ArcaneSignet.class)); - cards.add(new SetCardInfo("Archivist of Gondor", 18, Rarity.RARE, mage.cards.a.ArchivistOfGondor.class)); - cards.add(new SetCardInfo("Arwen, Weaver of Hope", 35, Rarity.RARE, mage.cards.a.ArwenWeaverOfHope.class)); + cards.add(new SetCardInfo("Archivist of Gondor", 420, Rarity.RARE, mage.cards.a.ArchivistOfGondor.class)); + cards.add(new SetCardInfo("Arwen, Weaver of Hope", 437, Rarity.RARE, mage.cards.a.ArwenWeaverOfHope.class)); cards.add(new SetCardInfo("Asceticism", 233, Rarity.RARE, mage.cards.a.Asceticism.class)); cards.add(new SetCardInfo("Ash Barrens", 295, Rarity.UNCOMMON, mage.cards.a.AshBarrens.class)); - cards.add(new SetCardInfo("Assemble the Entmoot", 36, Rarity.RARE, mage.cards.a.AssembleTheEntmoot.class)); + cards.add(new SetCardInfo("Assemble the Entmoot", 119, Rarity.RARE, mage.cards.a.AssembleTheEntmoot.class)); cards.add(new SetCardInfo("Banishing Light", 161, Rarity.UNCOMMON, mage.cards.b.BanishingLight.class)); cards.add(new SetCardInfo("Banquet Guests", 47, Rarity.RARE, mage.cards.b.BanquetGuests.class)); cards.add(new SetCardInfo("Basalt Monolith", 274, Rarity.UNCOMMON, mage.cards.b.BasaltMonolith.class)); @@ -36,24 +38,25 @@ public final class TalesOfMiddleEarthCommander extends ExpansionSet { cards.add(new SetCardInfo("Battlefield Forge", 296, Rarity.RARE, mage.cards.b.BattlefieldForge.class)); cards.add(new SetCardInfo("Beast Within", 234, Rarity.UNCOMMON, mage.cards.b.BeastWithin.class)); cards.add(new SetCardInfo("Beregond of the Guard", 9, Rarity.RARE, mage.cards.b.BeregondOfTheGuard.class)); - cards.add(new SetCardInfo("Bilbo, Birthday Celebrant", 48, Rarity.RARE, mage.cards.b.BilboBirthdayCelebrant.class)); + cards.add(new SetCardInfo("Bilbo, Birthday Celebrant", 451, Rarity.RARE, mage.cards.b.BilboBirthdayCelebrant.class)); cards.add(new SetCardInfo("Birds of Paradise", 235, Rarity.RARE, mage.cards.b.BirdsOfParadise.class)); cards.add(new SetCardInfo("Blasphemous Act", 211, Rarity.RARE, mage.cards.b.BlasphemousAct.class)); cards.add(new SetCardInfo("Bojuka Bog", 358, Rarity.MYTHIC, mage.cards.b.BojukaBog.class)); cards.add(new SetCardInfo("Boon of the Wish-Giver", 185, Rarity.UNCOMMON, mage.cards.b.BoonOfTheWishGiver.class)); - cards.add(new SetCardInfo("Boromir, Gondor's Hope", 49, Rarity.RARE, mage.cards.b.BoromirGondorsHope.class)); + cards.add(new SetCardInfo("Boromir, Gondor's Hope", 452, Rarity.RARE, mage.cards.b.BoromirGondorsHope.class)); cards.add(new SetCardInfo("Boseiju, Who Shelters All", 359, Rarity.MYTHIC, mage.cards.b.BoseijuWhoSheltersAll.class)); cards.add(new SetCardInfo("Brushland", 297, Rarity.RARE, mage.cards.b.Brushland.class)); cards.add(new SetCardInfo("Cabal Coffers", 360, Rarity.MYTHIC, mage.cards.c.CabalCoffers.class)); + cards.add(new SetCardInfo("Call Forth the Tempest", 509, Rarity.RARE, mage.cards.c.CallForthTheTempest.class)); cards.add(new SetCardInfo("Call for Unity", 163, Rarity.RARE, mage.cards.c.CallForUnity.class)); cards.add(new SetCardInfo("Canopy Vista", 298, Rarity.RARE, mage.cards.c.CanopyVista.class)); cards.add(new SetCardInfo("Castle Ardenvale", 361, Rarity.MYTHIC, mage.cards.c.CastleArdenvale.class)); - cards.add(new SetCardInfo("Cavern of Souls", 362, Rarity.MYTHIC, mage.cards.c.CavernOfSouls.class)); - cards.add(new SetCardInfo("Cavern-Hoard Dragon", 31, Rarity.RARE, mage.cards.c.CavernHoardDragon.class)); + cards.add(new SetCardInfo("Cavern of Souls", "392z", Rarity.MYTHIC, mage.cards.c.CavernOfSouls.class)); + cards.add(new SetCardInfo("Cavern-Hoard Dragon", 114, Rarity.RARE, mage.cards.c.CavernHoardDragon.class)); cards.add(new SetCardInfo("Champions of Minas Tirith", 10, Rarity.RARE, mage.cards.c.ChampionsOfMinasTirith.class)); cards.add(new SetCardInfo("Choked Estuary", 299, Rarity.RARE, mage.cards.c.ChokedEstuary.class)); cards.add(new SetCardInfo("Chromatic Lantern", 275, Rarity.RARE, mage.cards.c.ChromaticLantern.class)); - cards.add(new SetCardInfo("Cirdan the Shipwright", 50, Rarity.RARE, mage.cards.c.CirdanTheShipwright.class)); + cards.add(new SetCardInfo("Cirdan the Shipwright", 133, Rarity.RARE, mage.cards.c.CirdanTheShipwright.class)); cards.add(new SetCardInfo("Clifftop Retreat", 300, Rarity.RARE, mage.cards.c.ClifftopRetreat.class)); cards.add(new SetCardInfo("Cloudstone Curio", 349, Rarity.MYTHIC, mage.cards.c.CloudstoneCurio.class)); cards.add(new SetCardInfo("Colossal Whale", 186, Rarity.RARE, mage.cards.c.ColossalWhale.class)); @@ -75,35 +78,40 @@ public final class TalesOfMiddleEarthCommander extends ExpansionSet { cards.add(new SetCardInfo("Deserted Temple", 363, Rarity.MYTHIC, mage.cards.d.DesertedTemple.class)); cards.add(new SetCardInfo("Desolate Lighthouse", 303, Rarity.RARE, mage.cards.d.DesolateLighthouse.class)); cards.add(new SetCardInfo("Devastation Tide", 189, Rarity.RARE, mage.cards.d.DevastationTide.class)); + cards.add(new SetCardInfo("Diabolic Intent", 526, Rarity.MYTHIC, mage.cards.d.DiabolicIntent.class)); cards.add(new SetCardInfo("Door of Destinies", 277, Rarity.RARE, mage.cards.d.DoorOfDestinies.class)); + cards.add(new SetCardInfo("Doran, the Siege Tower", 517, Rarity.RARE, mage.cards.d.DoranTheSiegeTower.class)); cards.add(new SetCardInfo("Dragonskull Summit", 304, Rarity.RARE, mage.cards.d.DragonskullSummit.class)); cards.add(new SetCardInfo("Drowned Catacomb", 305, Rarity.RARE, mage.cards.d.DrownedCatacomb.class)); cards.add(new SetCardInfo("Dusk // Dawn", 166, Rarity.RARE, mage.cards.d.DuskDawn.class)); cards.add(new SetCardInfo("Earthquake", 214, Rarity.RARE, mage.cards.e.Earthquake.class)); - cards.add(new SetCardInfo("Elrond of the White Council", 51, Rarity.RARE, mage.cards.e.ElrondOfTheWhiteCouncil.class)); + cards.add(new SetCardInfo("Elrond of the White Council", 134, Rarity.RARE, mage.cards.e.ElrondOfTheWhiteCouncil.class)); cards.add(new SetCardInfo("Elvish Archdruid", 237, Rarity.RARE, mage.cards.e.ElvishArchdruid.class)); + cards.add(new SetCardInfo("Elvish Harbinger", 527, Rarity.UNCOMMON, mage.cards.e.ElvishHarbinger.class)); cards.add(new SetCardInfo("Elvish Mystic", 238, Rarity.COMMON, mage.cards.e.ElvishMystic.class)); cards.add(new SetCardInfo("Elvish Piper", 239, Rarity.RARE, mage.cards.e.ElvishPiper.class)); cards.add(new SetCardInfo("Elvish Visionary", 240, Rarity.COMMON, mage.cards.e.ElvishVisionary.class)); cards.add(new SetCardInfo("Elvish Warmaster", 241, Rarity.RARE, mage.cards.e.ElvishWarmaster.class)); cards.add(new SetCardInfo("Ensnaring Bridge", 350, Rarity.MYTHIC, mage.cards.e.EnsnaringBridge.class)); - cards.add(new SetCardInfo("Eomer, King of Rohan", 52, Rarity.RARE, mage.cards.e.EomerKingOfRohan.class)); - cards.add(new SetCardInfo("Eowyn, Shieldmaiden", 1, Rarity.MYTHIC, mage.cards.e.EowynShieldmaiden.class)); - cards.add(new SetCardInfo("Erestor of the Council", 53, Rarity.RARE, mage.cards.e.ErestorOfTheCouncil.class)); + cards.add(new SetCardInfo("Eomer, King of Rohan", 135, Rarity.RARE, mage.cards.e.EomerKingOfRohan.class)); + cards.add(new SetCardInfo("Eowyn, Shieldmaiden", 81, Rarity.MYTHIC, mage.cards.e.EowynShieldmaiden.class)); + cards.add(new SetCardInfo("Erestor of the Council", 457, Rarity.RARE, mage.cards.e.ErestorOfTheCouncil.class)); cards.add(new SetCardInfo("Essence Warden", 242, Rarity.COMMON, mage.cards.e.EssenceWarden.class)); cards.add(new SetCardInfo("Everflowing Chalice", 278, Rarity.UNCOMMON, mage.cards.e.EverflowingChalice.class)); cards.add(new SetCardInfo("Evolving Wilds", 306, Rarity.COMMON, mage.cards.e.EvolvingWilds.class)); cards.add(new SetCardInfo("Exotic Orchard", 307, Rarity.RARE, mage.cards.e.ExoticOrchard.class)); + cards.add(new SetCardInfo("Explore", 528, Rarity.UNCOMMON, mage.cards.e.Explore.class)); cards.add(new SetCardInfo("Extract from Darkness", 266, Rarity.UNCOMMON, mage.cards.e.ExtractFromDarkness.class)); cards.add(new SetCardInfo("Fact or Fiction", 190, Rarity.UNCOMMON, mage.cards.f.FactOrFiction.class)); cards.add(new SetCardInfo("Faithless Looting", 215, Rarity.COMMON, mage.cards.f.FaithlessLooting.class)); - cards.add(new SetCardInfo("Faramir, Steward of Gondor", 54, Rarity.RARE, mage.cards.f.FaramirStewardOfGondor.class)); + cards.add(new SetCardInfo("Faramir, Steward of Gondor", 458, Rarity.RARE, mage.cards.f.FaramirStewardOfGondor.class)); cards.add(new SetCardInfo("Farhaven Elf", 243, Rarity.COMMON, mage.cards.f.FarhavenElf.class)); - cards.add(new SetCardInfo("Farmer Cotton", 55, Rarity.RARE, mage.cards.f.FarmerCotton.class)); + cards.add(new SetCardInfo("Farmer Cotton", 459, Rarity.RARE, mage.cards.f.FarmerCotton.class)); cards.add(new SetCardInfo("Farseek", 244, Rarity.COMMON, mage.cards.f.Farseek.class)); cards.add(new SetCardInfo("Fealty to the Realm", 21, Rarity.RARE, mage.cards.f.FealtyToTheRealm.class)); cards.add(new SetCardInfo("Feasting Hobbit", 37, Rarity.RARE, mage.cards.f.FeastingHobbit.class)); cards.add(new SetCardInfo("Feed the Swarm", 200, Rarity.COMMON, mage.cards.f.FeedTheSwarm.class)); + cards.add(new SetCardInfo("Fell Beast of Mordor", 513, Rarity.RARE, mage.cards.f.FellBeastOfMordor.class)); cards.add(new SetCardInfo("Fell the Mighty", 167, Rarity.RARE, mage.cards.f.FellTheMighty.class)); cards.add(new SetCardInfo("Field of Ruin", 308, Rarity.UNCOMMON, mage.cards.f.FieldOfRuin.class)); cards.add(new SetCardInfo("Field-Tested Frying Pan", 11, Rarity.RARE, mage.cards.f.FieldTestedFryingPan.class)); @@ -112,7 +120,7 @@ public final class TalesOfMiddleEarthCommander extends ExpansionSet { cards.add(new SetCardInfo("Flooded Grove", 309, Rarity.RARE, mage.cards.f.FloodedGrove.class)); cards.add(new SetCardInfo("Forbidden Alchemy", 191, Rarity.COMMON, mage.cards.f.ForbiddenAlchemy.class)); cards.add(new SetCardInfo("Foreboding Ruins", 310, Rarity.RARE, mage.cards.f.ForebodingRuins.class)); - cards.add(new SetCardInfo("Forth Eorlingas!", 56, Rarity.RARE, mage.cards.f.ForthEorlingas.class)); + cards.add(new SetCardInfo("Forth Eorlingas!", 139, Rarity.RARE, mage.cards.f.ForthEorlingas.class)); cards.add(new SetCardInfo("Fortified Village", 311, Rarity.RARE, mage.cards.f.FortifiedVillage.class)); cards.add(new SetCardInfo("Frodo, Adventurous Hobbit", 2, Rarity.MYTHIC, mage.cards.f.FrodoAdventurousHobbit.class)); cards.add(new SetCardInfo("Frontier Warmonger", 217, Rarity.RARE, mage.cards.f.FrontierWarmonger.class)); @@ -121,29 +129,30 @@ public final class TalesOfMiddleEarthCommander extends ExpansionSet { cards.add(new SetCardInfo("Fumigate", 170, Rarity.RARE, mage.cards.f.Fumigate.class)); cards.add(new SetCardInfo("Furycalm Snarl", 313, Rarity.RARE, mage.cards.f.FurycalmSnarl.class)); cards.add(new SetCardInfo("Galadhrim Ambush", 38, Rarity.RARE, mage.cards.g.GaladhrimAmbush.class)); - cards.add(new SetCardInfo("Gandalf, Westward Voyager", 6, Rarity.MYTHIC, mage.cards.g.GandalfWestwardVoyager.class)); - cards.add(new SetCardInfo("Galadriel, Elven-Queen", 3, Rarity.MYTHIC, mage.cards.g.GaladrielElvenQueen.class)); + cards.add(new SetCardInfo("Galadhrim Brigade", 502, Rarity.RARE, mage.cards.g.GaladhrimBrigade.class)); + cards.add(new SetCardInfo("Galadriel, Elven-Queen", 83, Rarity.MYTHIC, mage.cards.g.GaladrielElvenQueen.class)); + cards.add(new SetCardInfo("Galadriel, Light of Valinor", 498, Rarity.MYTHIC, mage.cards.g.GaladrielLightOfValinor.class)); + cards.add(new SetCardInfo("Gandalf, Westward Voyager", 89, Rarity.MYTHIC, mage.cards.g.GandalfWestwardVoyager.class)); cards.add(new SetCardInfo("Gemstone Caverns", 364, Rarity.MYTHIC, mage.cards.g.GemstoneCaverns.class)); cards.add(new SetCardInfo("Genesis Wave", 245, Rarity.RARE, mage.cards.g.GenesisWave.class)); cards.add(new SetCardInfo("Ghost Quarter", 314, Rarity.UNCOMMON, mage.cards.g.GhostQuarter.class)); cards.add(new SetCardInfo("Gilded Goose", 246, Rarity.RARE, mage.cards.g.GildedGoose.class)); - cards.add(new SetCardInfo("Gilraen, Dunedain Protector", 13, Rarity.RARE, mage.cards.g.GilraenDunedainProtector.class)); - cards.add(new SetCardInfo("Gimli of the Glittering Caves", 32, Rarity.RARE, mage.cards.g.GimliOfTheGlitteringCaves.class)); + cards.add(new SetCardInfo("Gilraen, Dunedain Protector", 97, Rarity.RARE, mage.cards.g.GilraenDunedainProtector.class)); + cards.add(new SetCardInfo("Gimli of the Glittering Caves", 115, Rarity.RARE, mage.cards.g.GimliOfTheGlitteringCaves.class)); cards.add(new SetCardInfo("Glacial Fortress", 315, Rarity.RARE, mage.cards.g.GlacialFortress.class)); cards.add(new SetCardInfo("Go for the Throat", 201, Rarity.UNCOMMON, mage.cards.g.GoForTheThroat.class)); cards.add(new SetCardInfo("Goblin Cratermaker", 218, Rarity.UNCOMMON, mage.cards.g.GoblinCratermaker.class)); cards.add(new SetCardInfo("Goblin Dark-Dwellers", 219, Rarity.RARE, mage.cards.g.GoblinDarkDwellers.class)); - cards.add(new SetCardInfo("Gollum, Obsessed Stalker", 26, Rarity.RARE, mage.cards.g.GollumObsessedStalker.class)); + cards.add(new SetCardInfo("Gollum, Obsessed Stalker", 428, Rarity.RARE, mage.cards.g.GollumObsessedStalker.class)); cards.add(new SetCardInfo("Graypelt Refuge", 316, Rarity.UNCOMMON, mage.cards.g.GraypeltRefuge.class)); cards.add(new SetCardInfo("Great Oak Guardian", 247, Rarity.UNCOMMON, mage.cards.g.GreatOakGuardian.class)); - cards.add(new SetCardInfo("Grey Host Reinforcements", 14, Rarity.RARE, mage.cards.g.GreyHostReinforcements.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Grey Host Reinforcements", 98, Rarity.RARE, mage.cards.g.GreyHostReinforcements.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Grima, Saruman's Footman", 57, Rarity.RARE, mage.cards.g.GrimaSarumansFootman.class)); + cards.add(new SetCardInfo("Grey Host Reinforcements", 416, Rarity.RARE, mage.cards.g.GreyHostReinforcements.class)); + cards.add(new SetCardInfo("Grima, Saruman's Footman", 140, Rarity.RARE, mage.cards.g.GrimaSarumansFootman.class)); cards.add(new SetCardInfo("Growth Spiral", 267, Rarity.COMMON, mage.cards.g.GrowthSpiral.class)); cards.add(new SetCardInfo("Guttersnipe", 220, Rarity.UNCOMMON, mage.cards.g.Guttersnipe.class)); - cards.add(new SetCardInfo("Gwaihir, Greatest of the Eagles", 15, Rarity.RARE, mage.cards.g.GwaihirGreatestOfTheEagles.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gwaihir, Greatest of the Eagles", 99, Rarity.RARE, mage.cards.g.GwaihirGreatestOfTheEagles.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gwaihir, Greatest of the Eagles", 15, Rarity.RARE, mage.cards.g.GwaihirGreatestOfTheEagles.class)); cards.add(new SetCardInfo("Haldir, Lorien Lieutenant", 39, Rarity.RARE, mage.cards.h.HaldirLorienLieutenant.class)); + cards.add(new SetCardInfo("Hammerheim", 518, Rarity.RARE, mage.cards.h.Hammerheim.class)); cards.add(new SetCardInfo("Harmonize", 248, Rarity.UNCOMMON, mage.cards.h.Harmonize.class)); cards.add(new SetCardInfo("Harsh Mentor", 221, Rarity.RARE, mage.cards.h.HarshMentor.class)); cards.add(new SetCardInfo("Heirloom Blade", 279, Rarity.UNCOMMON, mage.cards.h.HeirloomBlade.class)); @@ -159,49 +168,53 @@ public final class TalesOfMiddleEarthCommander extends ExpansionSet { cards.add(new SetCardInfo("Increasing Devotion", 171, Rarity.RARE, mage.cards.i.IncreasingDevotion.class)); cards.add(new SetCardInfo("Inferno Titan", 223, Rarity.MYTHIC, mage.cards.i.InfernoTitan.class)); cards.add(new SetCardInfo("Inscription of Abundance", 251, Rarity.RARE, mage.cards.i.InscriptionOfAbundance.class)); + cards.add(new SetCardInfo("Ishkanah, Grafwidow", 516, Rarity.MYTHIC, mage.cards.i.IshkanahGrafwidow.class)); cards.add(new SetCardInfo("Isolated Chapel", 318, Rarity.RARE, mage.cards.i.IsolatedChapel.class)); cards.add(new SetCardInfo("Karakas", 367, Rarity.MYTHIC, mage.cards.k.Karakas.class)); + cards.add(new SetCardInfo("Kenrith, the Returned King", 515, Rarity.RARE, mage.cards.k.KenrithTheReturnedKing.class)); cards.add(new SetCardInfo("Knollspine Dragon", 224, Rarity.RARE, mage.cards.k.KnollspineDragon.class)); cards.add(new SetCardInfo("Kor Haven", 368, Rarity.MYTHIC, mage.cards.k.KorHaven.class)); cards.add(new SetCardInfo("Languish", 202, Rarity.RARE, mage.cards.l.Languish.class)); cards.add(new SetCardInfo("Learn from the Past", 192, Rarity.UNCOMMON, mage.cards.l.LearnFromThePast.class)); - cards.add(new SetCardInfo("Legolas Greenleaf", 40, Rarity.RARE, mage.cards.l.LegolasGreenleaf.class)); + cards.add(new SetCardInfo("Legolas Greenleaf", 442, Rarity.RARE, mage.cards.l.LegolasGreenleaf.class)); + cards.add(new SetCardInfo("Legolas's Quick Reflexes", 493, Rarity.RARE, mage.cards.l.LegolassQuickReflexes.class)); cards.add(new SetCardInfo("Lidless Gaze", 59, Rarity.RARE, mage.cards.l.LidlessGaze.class)); cards.add(new SetCardInfo("Lightning Greaves", 281, Rarity.UNCOMMON, mage.cards.l.LightningGreaves.class)); cards.add(new SetCardInfo("Lignify", 252, Rarity.COMMON, mage.cards.l.Lignify.class)); cards.add(new SetCardInfo("Living Death", 203, Rarity.RARE, mage.cards.l.LivingDeath.class)); cards.add(new SetCardInfo("Lobelia, Defender of Bag End", 27, Rarity.RARE, mage.cards.l.LobeliaDefenderOfBagEnd.class)); cards.add(new SetCardInfo("Lonely Sandbar", 319, Rarity.UNCOMMON, mage.cards.l.LonelySandbar.class)); - cards.add(new SetCardInfo("Lord of the Nazgul", 60, Rarity.RARE, mage.cards.l.LordOfTheNazgul.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Lord of the Nazgul", 142, Rarity.RARE, mage.cards.l.LordOfTheNazgul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lord of the Nazgul", 467, Rarity.RARE, mage.cards.l.LordOfTheNazgul.class)); cards.add(new SetCardInfo("Lossarnach Captain", 16, Rarity.RARE, mage.cards.l.LossarnachCaptain.class)); - cards.add(new SetCardInfo("Lothlorien Blade", 77, Rarity.RARE, mage.cards.l.LothlorienBlade.class)); + cards.add(new SetCardInfo("Lothlorien Blade", 487, Rarity.RARE, mage.cards.l.LothlorienBlade.class)); cards.add(new SetCardInfo("Marshal's Anthem", 172, Rarity.RARE, mage.cards.m.MarshalsAnthem.class)); cards.add(new SetCardInfo("Mentor of the Meek", 173, Rarity.RARE, mage.cards.m.MentorOfTheMeek.class)); cards.add(new SetCardInfo("Merciless Executioner", 204, Rarity.UNCOMMON, mage.cards.m.MercilessExecutioner.class)); - cards.add(new SetCardInfo("Merry, Warden of Isengard", 61, Rarity.RARE, mage.cards.m.MerryWardenOfIsengard.class)); + cards.add(new SetCardInfo("Merry, Warden of Isengard", 143, Rarity.RARE, mage.cards.m.MerryWardenOfIsengard.class)); cards.add(new SetCardInfo("Minamo, School at Water's Edge", 369, Rarity.MYTHIC, mage.cards.m.MinamoSchoolAtWatersEdge.class)); cards.add(new SetCardInfo("Mind Stone", 282, Rarity.UNCOMMON, mage.cards.m.MindStone.class)); - cards.add(new SetCardInfo("Mirkwood Elk", 41, Rarity.RARE, mage.cards.m.MirkwoodElk.class)); + cards.add(new SetCardInfo("Mirkwood Elk", 443, Rarity.RARE, mage.cards.m.MirkwoodElk.class)); cards.add(new SetCardInfo("Mirkwood Trapper", 62, Rarity.RARE, mage.cards.m.MirkwoodTrapper.class)); - cards.add(new SetCardInfo("Model of Unity", 78, Rarity.RARE, mage.cards.m.ModelOfUnity.class)); - cards.add(new SetCardInfo("Monstrosity of the Lake", 22, Rarity.RARE, mage.cards.m.MonstrosityOfTheLake.class)); + cards.add(new SetCardInfo("Model of Unity", 488, Rarity.RARE, mage.cards.m.ModelOfUnity.class)); + cards.add(new SetCardInfo("Monstrosity of the Lake", 424, Rarity.RARE, mage.cards.m.MonstrosityOfTheLake.class)); cards.add(new SetCardInfo("Moria Scavenger", 63, Rarity.RARE, mage.cards.m.MoriaScavenger.class)); cards.add(new SetCardInfo("Mortify", 269, Rarity.UNCOMMON, mage.cards.m.Mortify.class)); - cards.add(new SetCardInfo("Motivated Pony", 42, Rarity.RARE, mage.cards.m.MotivatedPony.class)); + cards.add(new SetCardInfo("Motivated Pony", 444, Rarity.RARE, mage.cards.m.MotivatedPony.class)); cards.add(new SetCardInfo("Mouth of Ronom", 370, Rarity.MYTHIC, mage.cards.m.MouthOfRonom.class)); cards.add(new SetCardInfo("Murmuring Bosk", 320, Rarity.RARE, mage.cards.m.MurmuringBosk.class)); + cards.add(new SetCardInfo("Myriad Landscape", 534, Rarity.UNCOMMON, mage.cards.m.MyriadLandscape.class)); cards.add(new SetCardInfo("Mystic Confluence", 193, Rarity.RARE, mage.cards.m.MysticConfluence.class)); cards.add(new SetCardInfo("Necroblossom Snarl", 321, Rarity.RARE, mage.cards.n.NecroblossomSnarl.class)); cards.add(new SetCardInfo("Night's Whisper", 205, Rarity.COMMON, mage.cards.n.NightsWhisper.class)); cards.add(new SetCardInfo("Notion Thief", 270, Rarity.RARE, mage.cards.n.NotionThief.class)); - cards.add(new SetCardInfo("Oath of Eorl", 64, Rarity.RARE, mage.cards.o.OathOfEorl.class)); + cards.add(new SetCardInfo("Oath of Eorl", 471, Rarity.RARE, mage.cards.o.OathOfEorl.class)); cards.add(new SetCardInfo("Oboro, Palace in the Clouds", 371, Rarity.MYTHIC, mage.cards.o.OboroPalaceInTheClouds.class)); cards.add(new SetCardInfo("Of Herbs and Stewed Rabbit", 17, Rarity.RARE, mage.cards.o.OfHerbsAndStewedRabbit.class)); cards.add(new SetCardInfo("Opt", 194, Rarity.COMMON, mage.cards.o.Opt.class)); cards.add(new SetCardInfo("Orchard Strider", 253, Rarity.COMMON, mage.cards.o.OrchardStrider.class)); - cards.add(new SetCardInfo("Orcish Siegemaster", 33, Rarity.RARE, mage.cards.o.OrcishSiegemaster.class)); + cards.add(new SetCardInfo("Orcish Siegemaster", 116, Rarity.RARE, mage.cards.o.OrcishSiegemaster.class)); cards.add(new SetCardInfo("Overwhelming Stampede", 254, Rarity.RARE, mage.cards.o.OverwhelmingStampede.class)); + cards.add(new SetCardInfo("Pact of Negation", 523, Rarity.RARE, mage.cards.p.PactOfNegation.class)); cards.add(new SetCardInfo("Palace Jailer", 174, Rarity.UNCOMMON, mage.cards.p.PalaceJailer.class)); cards.add(new SetCardInfo("Paradise Druid", 255, Rarity.UNCOMMON, mage.cards.p.ParadiseDruid.class)); cards.add(new SetCardInfo("Path of Ancestry", 322, Rarity.COMMON, mage.cards.p.PathOfAncestry.class)); @@ -213,52 +226,55 @@ public final class TalesOfMiddleEarthCommander extends ExpansionSet { cards.add(new SetCardInfo("Prairie Stream", 324, Rarity.RARE, mage.cards.p.PrairieStream.class)); cards.add(new SetCardInfo("Preordain", 196, Rarity.COMMON, mage.cards.p.Preordain.class)); cards.add(new SetCardInfo("Pristine Talisman", 283, Rarity.COMMON, mage.cards.p.PristineTalisman.class)); - cards.add(new SetCardInfo("Prize Pig", 43, Rarity.RARE, mage.cards.p.PrizePig.class)); + cards.add(new SetCardInfo("Prize Pig", 126, Rarity.RARE, mage.cards.p.PrizePig.class)); cards.add(new SetCardInfo("Prosperous Innkeeper", 256, Rarity.UNCOMMON, mage.cards.p.ProsperousInnkeeper.class)); cards.add(new SetCardInfo("Radagast, Wizard of Wilds", 66, Rarity.RARE, mage.cards.r.RadagastWizardOfWilds.class)); - cards.add(new SetCardInfo("Raise the Palisade", 23, Rarity.RARE, mage.cards.r.RaiseThePalisade.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Raise the Palisade", 106, Rarity.RARE, mage.cards.r.RaiseThePalisade.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Rampaging War Mammoth", 34, Rarity.RARE, mage.cards.r.RampagingWarMammoth.class)); + cards.add(new SetCardInfo("Raise the Palisade", 23, Rarity.RARE, mage.cards.r.RaiseThePalisade.class)); + cards.add(new SetCardInfo("Rampaging War Mammoth", 436, Rarity.RARE, mage.cards.r.RampagingWarMammoth.class)); cards.add(new SetCardInfo("Rampant Growth", 257, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); - cards.add(new SetCardInfo("Rapacious Guest", 28, Rarity.RARE, mage.cards.r.RapaciousGuest.class)); + cards.add(new SetCardInfo("Rapacious Guest", 111, Rarity.RARE, mage.cards.r.RapaciousGuest.class)); cards.add(new SetCardInfo("Realm Seekers", 258, Rarity.RARE, mage.cards.r.RealmSeekers.class)); cards.add(new SetCardInfo("Reanimate", 206, Rarity.RARE, mage.cards.r.Reanimate.class)); cards.add(new SetCardInfo("Reclamation Sage", 259, Rarity.UNCOMMON, mage.cards.r.ReclamationSage.class)); cards.add(new SetCardInfo("Reflecting Pool", 373, Rarity.MYTHIC, mage.cards.r.ReflectingPool.class)); cards.add(new SetCardInfo("Rejuvenating Springs", 325, Rarity.RARE, mage.cards.r.RejuvenatingSprings.class)); - cards.add(new SetCardInfo("Relic of Sauron", 79, Rarity.RARE, mage.cards.r.RelicOfSauron.class)); + cards.add(new SetCardInfo("Relic of Sauron", 489, Rarity.RARE, mage.cards.r.RelicOfSauron.class)); cards.add(new SetCardInfo("Revenge of Ravens", 207, Rarity.UNCOMMON, mage.cards.r.RevengeOfRavens.class)); - cards.add(new SetCardInfo("Riders of Rohan", 67, Rarity.RARE, mage.cards.r.RidersOfRohan.class)); + cards.add(new SetCardInfo("Riders of Rohan", 474, Rarity.RARE, mage.cards.r.RidersOfRohan.class)); cards.add(new SetCardInfo("Rings of Brighthearth", 352, Rarity.MYTHIC, mage.cards.r.RingsOfBrighthearth.class)); + cards.add(new SetCardInfo("River Kelpie", 524, Rarity.RARE, mage.cards.r.RiverKelpie.class)); cards.add(new SetCardInfo("Rogue's Passage", 326, Rarity.UNCOMMON, mage.cards.r.RoguesPassage.class)); - cards.add(new SetCardInfo("Sail into the West", 68, Rarity.RARE, mage.cards.s.SailIntoTheWest.class)); - cards.add(new SetCardInfo("Sam, Loyal Attendant", 7, Rarity.MYTHIC, mage.cards.s.SamLoyalAttendant.class)); + cards.add(new SetCardInfo("Sail into the West", 149, Rarity.RARE, mage.cards.s.SailIntoTheWest.class)); + cards.add(new SetCardInfo("Sam, Loyal Attendant", 90, Rarity.MYTHIC, mage.cards.s.SamLoyalAttendant.class)); cards.add(new SetCardInfo("Sandsteppe Citadel", 327, Rarity.UNCOMMON, mage.cards.s.SandsteppeCitadel.class)); cards.add(new SetCardInfo("Sanguine Bond", 208, Rarity.RARE, mage.cards.s.SanguineBond.class)); cards.add(new SetCardInfo("Saruman, the White Hand", 8, Rarity.MYTHIC, mage.cards.s.SarumanTheWhiteHand.class)); - cards.add(new SetCardInfo("Sauron, Lord of the Rings", 4, Rarity.MYTHIC, mage.cards.s.SauronLordOfTheRings.class)); + cards.add(new SetCardInfo("Sauron, Lord of the Rings", 478, Rarity.MYTHIC, mage.cards.s.SauronLordOfTheRings.class)); cards.add(new SetCardInfo("Savvy Hunter", 271, Rarity.UNCOMMON, mage.cards.s.SavvyHunter.class)); cards.add(new SetCardInfo("Scattered Groves", 328, Rarity.RARE, mage.cards.s.ScatteredGroves.class)); cards.add(new SetCardInfo("Scoured Barrens", 329, Rarity.COMMON, mage.cards.s.ScouredBarrens.class)); cards.add(new SetCardInfo("Scourge of the Throne", 225, Rarity.MYTHIC, mage.cards.s.ScourgeOfTheThrone.class)); + cards.add(new SetCardInfo("Seasons Past", 529, Rarity.MYTHIC, mage.cards.s.SeasonsPast.class)); cards.add(new SetCardInfo("Secluded Courtyard", 330, Rarity.UNCOMMON, mage.cards.s.SecludedCourtyard.class)); + cards.add(new SetCardInfo("Second Harvest", 530, Rarity.RARE, mage.cards.s.SecondHarvest.class)); cards.add(new SetCardInfo("Seeds of Renewal", 260, Rarity.RARE, mage.cards.s.SeedsOfRenewal.class)); cards.add(new SetCardInfo("Selfless Squire", 176, Rarity.RARE, mage.cards.s.SelflessSquire.class)); cards.add(new SetCardInfo("Shadowspear", 353, Rarity.MYTHIC, mage.cards.s.Shadowspear.class)); cards.add(new SetCardInfo("Shared Animosity", 226, Rarity.RARE, mage.cards.s.SharedAnimosity.class)); - cards.add(new SetCardInfo("Shelob, Dread Weaver", 29, Rarity.RARE, mage.cards.s.ShelobDreadWeaver.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Shelob, Dread Weaver", 112, Rarity.RARE, mage.cards.s.ShelobDreadWeaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shelob, Dread Weaver", 112, Rarity.RARE, mage.cards.s.ShelobDreadWeaver.class)); cards.add(new SetCardInfo("Shineshadow Snarl", 331, Rarity.RARE, mage.cards.s.ShineshadowSnarl.class)); cards.add(new SetCardInfo("Shinka, the Bloodsoaked Keep", 374, Rarity.MYTHIC, mage.cards.s.ShinkaTheBloodsoakedKeep.class)); cards.add(new SetCardInfo("Shiny Impetus", 227, Rarity.UNCOMMON, mage.cards.s.ShinyImpetus.class)); cards.add(new SetCardInfo("Siege-Gang Commander", 228, Rarity.RARE, mage.cards.s.SiegeGangCommander.class)); cards.add(new SetCardInfo("Smoldering Marsh", 332, Rarity.RARE, mage.cards.s.SmolderingMarsh.class)); cards.add(new SetCardInfo("Sol Ring", 284, Rarity.UNCOMMON, mage.cards.s.SolRing.class)); - cards.add(new SetCardInfo("Song of Earendil", 69, Rarity.RARE, mage.cards.s.SongOfEarendil.class)); - cards.add(new SetCardInfo("Subjugate the Hobbits", 24, Rarity.RARE, mage.cards.s.SubjugateTheHobbits.class)); + cards.add(new SetCardInfo("Song of Earendil", 479, Rarity.RARE, mage.cards.s.SongOfEarendil.class)); + cards.add(new SetCardInfo("Soul's Attendant", 520, Rarity.UNCOMMON, mage.cards.s.SoulsAttendant.class)); + cards.add(new SetCardInfo("Stonehewer Giant", 521, Rarity.RARE, mage.cards.s.StonehewerGiant.class)); + cards.add(new SetCardInfo("Subjugate the Hobbits", 107, Rarity.RARE, mage.cards.s.SubjugateTheHobbits.class)); cards.add(new SetCardInfo("Sulfur Falls", 333, Rarity.RARE, mage.cards.s.SulfurFalls.class)); cards.add(new SetCardInfo("Sulfurous Springs", 334, Rarity.RARE, mage.cards.s.SulfurousSprings.class)); - cards.add(new SetCardInfo("Summons of Saruman", 70, Rarity.RARE, mage.cards.s.SummonsOfSaruman.class)); + cards.add(new SetCardInfo("Summons of Saruman", 480, Rarity.RARE, mage.cards.s.SummonsOfSaruman.class)); cards.add(new SetCardInfo("Sunken Hollow", 335, Rarity.RARE, mage.cards.s.SunkenHollow.class)); cards.add(new SetCardInfo("Sunpetal Grove", 336, Rarity.RARE, mage.cards.s.SunpetalGrove.class)); cards.add(new SetCardInfo("Sunset Revelry", 177, Rarity.UNCOMMON, mage.cards.s.SunsetRevelry.class)); @@ -268,13 +284,15 @@ public final class TalesOfMiddleEarthCommander extends ExpansionSet { cards.add(new SetCardInfo("Sword of the Animist", 355, Rarity.MYTHIC, mage.cards.s.SwordOfTheAnimist.class)); cards.add(new SetCardInfo("Swords to Plowshares", 178, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class)); cards.add(new SetCardInfo("Sylvan Offering", 261, Rarity.RARE, mage.cards.s.SylvanOffering.class)); + cards.add(new SetCardInfo("Sylvan Tutor", 531, Rarity.MYTHIC, mage.cards.s.SylvanTutor.class)); cards.add(new SetCardInfo("Talisman of Conviction", 285, Rarity.UNCOMMON, mage.cards.t.TalismanOfConviction.class)); cards.add(new SetCardInfo("Talisman of Progress", 286, Rarity.UNCOMMON, mage.cards.t.TalismanOfProgress.class)); - cards.add(new SetCardInfo("Taunt from the Rampart", 71, Rarity.RARE, mage.cards.t.TauntFromTheRampart.class)); + cards.add(new SetCardInfo("Taunt from the Rampart", 481, Rarity.RARE, mage.cards.t.TauntFromTheRampart.class)); + cards.add(new SetCardInfo("Tempt with Discovery", 532, Rarity.UNCOMMON, mage.cards.t.TemptWithDiscovery.class)); cards.add(new SetCardInfo("Terramorphic Expanse", 337, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class)); cards.add(new SetCardInfo("The Balrog of Moria", 46, Rarity.RARE, mage.cards.t.TheBalrogOfMoria.class)); - cards.add(new SetCardInfo("The Black Gate", 80, Rarity.RARE, mage.cards.t.TheBlackGate.class)); - cards.add(new SetCardInfo("The Gaffer", 12, Rarity.RARE, mage.cards.t.TheGaffer.class)); + cards.add(new SetCardInfo("The Black Gate", 160, Rarity.RARE, mage.cards.t.TheBlackGate.class)); + cards.add(new SetCardInfo("The Gaffer", 414, Rarity.RARE, mage.cards.t.TheGaffer.class)); cards.add(new SetCardInfo("The Great Henge", 348, Rarity.MYTHIC, mage.cards.t.TheGreatHenge.class)); cards.add(new SetCardInfo("The Ozolith", 351, Rarity.MYTHIC, mage.cards.t.TheOzolith.class)); cards.add(new SetCardInfo("Thorn of Amethyst", 356, Rarity.MYTHIC, mage.cards.t.ThornOfAmethyst.class)); @@ -282,25 +300,27 @@ public final class TalesOfMiddleEarthCommander extends ExpansionSet { cards.add(new SetCardInfo("Thought Vessel", 287, Rarity.COMMON, mage.cards.t.ThoughtVessel.class)); cards.add(new SetCardInfo("Thrill of Possibility", 229, Rarity.COMMON, mage.cards.t.ThrillOfPossibility.class)); cards.add(new SetCardInfo("Throne of the High City", 339, Rarity.RARE, mage.cards.t.ThroneOfTheHighCity.class)); + cards.add(new SetCardInfo("Timber Protector", 533, Rarity.RARE, mage.cards.t.TimberProtector.class)); cards.add(new SetCardInfo("Tireless Provisioner", 262, Rarity.UNCOMMON, mage.cards.t.TirelessProvisioner.class)); cards.add(new SetCardInfo("Too Greedily, Too Deep", 72, Rarity.RARE, mage.cards.t.TooGreedilyTooDeep.class)); cards.add(new SetCardInfo("Toxic Deluge", 209, Rarity.RARE, mage.cards.t.ToxicDeluge.class)); cards.add(new SetCardInfo("Trading Post", 288, Rarity.RARE, mage.cards.t.TradingPost.class)); cards.add(new SetCardInfo("Tranquil Cove", 340, Rarity.COMMON, mage.cards.t.TranquilCove.class)); cards.add(new SetCardInfo("Tranquil Thicket", 341, Rarity.UNCOMMON, mage.cards.t.TranquilThicket.class)); - cards.add(new SetCardInfo("Trap the Trespassers", 25, Rarity.RARE, mage.cards.t.TrapTheTrespassers.class)); - cards.add(new SetCardInfo("Travel Through Caradhras", 44, Rarity.RARE, mage.cards.t.TravelThroughCaradhras.class)); + cards.add(new SetCardInfo("Trap the Trespassers", 108, Rarity.RARE, mage.cards.t.TrapTheTrespassers.class)); + cards.add(new SetCardInfo("Travel Through Caradhras", 127, Rarity.RARE, mage.cards.t.TravelThroughCaradhras.class)); cards.add(new SetCardInfo("Treasure Nabber", 230, Rarity.RARE, mage.cards.t.TreasureNabber.class)); - cards.add(new SetCardInfo("Treebeard, Gracious Host", 73, Rarity.RARE, mage.cards.t.TreebeardGraciousHost.class)); + cards.add(new SetCardInfo("Treebeard, Gracious Host", 153, Rarity.RARE, mage.cards.t.TreebeardGraciousHost.class)); cards.add(new SetCardInfo("Unbreakable Formation", 179, Rarity.RARE, mage.cards.u.UnbreakableFormation.class)); cards.add(new SetCardInfo("Underground River", 342, Rarity.RARE, mage.cards.u.UndergroundRiver.class)); + cards.add(new SetCardInfo("Urborg", 519, Rarity.RARE, mage.cards.u.Urborg.class)); cards.add(new SetCardInfo("Urborg, Tomb of Yawgmoth", 375, Rarity.MYTHIC, mage.cards.u.UrborgTombOfYawgmoth.class)); cards.add(new SetCardInfo("Vanquisher's Banner", 289, Rarity.RARE, mage.cards.v.VanquishersBanner.class)); cards.add(new SetCardInfo("Verge Rangers", 180, Rarity.RARE, mage.cards.v.VergeRangers.class)); cards.add(new SetCardInfo("Village Bell-Ringer", 181, Rarity.COMMON, mage.cards.v.VillageBellRinger.class)); cards.add(new SetCardInfo("Vineglimmer Snarl", 343, Rarity.RARE, mage.cards.v.VineglimmerSnarl.class)); cards.add(new SetCardInfo("Visions of Glory", 182, Rarity.RARE, mage.cards.v.VisionsOfGlory.class)); - cards.add(new SetCardInfo("Wake the Dragon", 74, Rarity.RARE, mage.cards.w.WakeTheDragon.class)); + cards.add(new SetCardInfo("Wake the Dragon", 154, Rarity.RARE, mage.cards.w.WakeTheDragon.class)); cards.add(new SetCardInfo("Wasteland", 376, Rarity.MYTHIC, mage.cards.w.Wasteland.class)); cards.add(new SetCardInfo("Wayfarer's Bauble", 290, Rarity.COMMON, mage.cards.w.WayfarersBauble.class)); cards.add(new SetCardInfo("Weathered Wayfarer", 183, Rarity.RARE, mage.cards.w.WeatheredWayfarer.class)); @@ -308,12 +328,13 @@ public final class TalesOfMiddleEarthCommander extends ExpansionSet { cards.add(new SetCardInfo("Whispersilk Cloak", 292, Rarity.UNCOMMON, mage.cards.w.WhispersilkCloak.class)); cards.add(new SetCardInfo("Wind-Scarred Crag", 344, Rarity.COMMON, mage.cards.w.WindScarredCrag.class)); cards.add(new SetCardInfo("Windbrisk Heights", 345, Rarity.RARE, mage.cards.w.WindbriskHeights.class)); - cards.add(new SetCardInfo("Windswift Slice", 45, Rarity.RARE, mage.cards.w.WindswiftSlice.class)); + cards.add(new SetCardInfo("Windswift Slice", 447, Rarity.RARE, mage.cards.w.WindswiftSlice.class)); cards.add(new SetCardInfo("Wood Elves", 263, Rarity.COMMON, mage.cards.w.WoodElves.class)); cards.add(new SetCardInfo("Woodfall Primus", 264, Rarity.RARE, mage.cards.w.WoodfallPrimus.class)); cards.add(new SetCardInfo("Woodland Cemetery", 346, Rarity.RARE, mage.cards.w.WoodlandCemetery.class)); cards.add(new SetCardInfo("Woodland Stream", 347, Rarity.COMMON, mage.cards.w.WoodlandStream.class)); cards.add(new SetCardInfo("Worn Powerstone", 293, Rarity.UNCOMMON, mage.cards.w.WornPowerstone.class)); + cards.add(new SetCardInfo("Worship", 522, Rarity.RARE, mage.cards.w.Worship.class)); cards.add(new SetCardInfo("Yavimaya, Cradle of Growth", 377, Rarity.MYTHIC, mage.cards.y.YavimayaCradleOfGrowth.class)); cards.add(new SetCardInfo("Zealous Conscripts", 231, Rarity.RARE, mage.cards.z.ZealousConscripts.class)); } diff --git a/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java b/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java index 1921f718cbe..bed6b81a299 100644 --- a/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java +++ b/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java @@ -26,282 +26,501 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Anduril, Flame of the West", 236, Rarity.MYTHIC, mage.cards.a.AndurilFlameOfTheWest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Anduril, Flame of the West", 375, Rarity.MYTHIC, mage.cards.a.AndurilFlameOfTheWest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Anduril, Flame of the West", 687, Rarity.MYTHIC, mage.cards.a.AndurilFlameOfTheWest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Anduril, Flame of the West", 746, Rarity.MYTHIC, mage.cards.a.AndurilFlameOfTheWest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Anduril, Flame of the West", 786, Rarity.MYTHIC, mage.cards.a.AndurilFlameOfTheWest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn and Arwen, Wed", 287, Rarity.MYTHIC, mage.cards.a.AragornAndArwenWed.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn and Arwen, Wed", 394, Rarity.MYTHIC, mage.cards.a.AragornAndArwenWed.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn, Company Leader", 191, Rarity.RARE, mage.cards.a.AragornCompanyLeader.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn, Company Leader", 316, Rarity.RARE, mage.cards.a.AragornCompanyLeader.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn, Company Leader", 410, Rarity.RARE, mage.cards.a.AragornCompanyLeader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aragorn, Company Leader", 642, Rarity.RARE, mage.cards.a.AragornCompanyLeader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aragorn, Company Leader", 808, Rarity.RARE, mage.cards.a.AragornCompanyLeader.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn, the Uniter", 192, Rarity.MYTHIC, mage.cards.a.AragornTheUniter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn, the Uniter", 317, Rarity.MYTHIC, mage.cards.a.AragornTheUniter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn, the Uniter", 434, Rarity.MYTHIC, mage.cards.a.AragornTheUniter.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Arwen Undomiel", 194, Rarity.UNCOMMON, mage.cards.a.ArwenUndomiel.class)); - cards.add(new SetCardInfo("Arwen's Gift", 39, Rarity.COMMON, mage.cards.a.ArwensGift.class)); + cards.add(new SetCardInfo("Aragorn, the Uniter", 643, Rarity.MYTHIC, mage.cards.a.AragornTheUniter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aragorn, the Uniter", 741, Rarity.MYTHIC, mage.cards.a.AragornTheUniter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aragorn, the Uniter", 809, Rarity.MYTHIC, mage.cards.a.AragornTheUniter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arwen Undomiel", 194, Rarity.UNCOMMON, mage.cards.a.ArwenUndomiel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arwen Undomiel", 645, Rarity.UNCOMMON, mage.cards.a.ArwenUndomiel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arwen's Gift", 39, Rarity.COMMON, mage.cards.a.ArwensGift.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arwen's Gift", 490, Rarity.COMMON, mage.cards.a.ArwensGift.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arwen, Mortal Queen", 193, Rarity.MYTHIC, mage.cards.a.ArwenMortalQueen.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arwen, Mortal Queen", 367, Rarity.MYTHIC, mage.cards.a.ArwenMortalQueen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arwen, Mortal Queen", 644, Rarity.MYTHIC, mage.cards.a.ArwenMortalQueen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arwen, Mortal Queen", 742, Rarity.MYTHIC, mage.cards.a.ArwenMortalQueen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arwen, Mortal Queen", 778, Rarity.MYTHIC, mage.cards.a.ArwenMortalQueen.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Assault on Osgiliath", 285, Rarity.RARE, mage.cards.a.AssaultOnOsgiliath.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Assault on Osgiliath", 386, Rarity.RARE, mage.cards.a.AssaultOnOsgiliath.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Bag End Porter", 153, Rarity.COMMON, mage.cards.b.BagEndPorter.class)); - cards.add(new SetCardInfo("Banish from Edoras", 1, Rarity.COMMON, mage.cards.b.BanishFromEdoras.class)); + cards.add(new SetCardInfo("Bag End Porter", 153, Rarity.COMMON, mage.cards.b.BagEndPorter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bag End Porter", 604, Rarity.COMMON, mage.cards.b.BagEndPorter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Banish from Edoras", 1, Rarity.COMMON, mage.cards.b.BanishFromEdoras.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Banish from Edoras", 452, Rarity.COMMON, mage.cards.b.BanishFromEdoras.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Barad-dur", 253, Rarity.RARE, mage.cards.b.BaradDur.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Barad-dur", 340, Rarity.RARE, mage.cards.b.BaradDur.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Barad-dur", 425, Rarity.RARE, mage.cards.b.BaradDur.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Barrow-Blade", 237, Rarity.UNCOMMON, mage.cards.b.BarrowBlade.class)); - cards.add(new SetCardInfo("Battle-Scarred Goblin", 115, Rarity.COMMON, mage.cards.b.BattleScarredGoblin.class)); - cards.add(new SetCardInfo("Bewitching Leechcraft", 41, Rarity.COMMON, mage.cards.b.BewitchingLeechcraft.class)); + cards.add(new SetCardInfo("Barad-dur", 704, Rarity.RARE, mage.cards.b.BaradDur.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Barad-dur", 751, Rarity.RARE, mage.cards.b.BaradDur.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Barrow-Blade", 237, Rarity.UNCOMMON, mage.cards.b.BarrowBlade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Barrow-Blade", 688, Rarity.UNCOMMON, mage.cards.b.BarrowBlade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Battle-Scarred Goblin", 115, Rarity.COMMON, mage.cards.b.BattleScarredGoblin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Battle-Scarred Goblin", 566, Rarity.COMMON, mage.cards.b.BattleScarredGoblin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bewitching Leechcraft", 41, Rarity.COMMON, mage.cards.b.BewitchingLeechcraft.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bewitching Leechcraft", 492, Rarity.COMMON, mage.cards.b.BewitchingLeechcraft.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bilbo's Ring", 298, Rarity.RARE, mage.cards.b.BilbosRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bilbo's Ring", 397, Rarity.RARE, mage.cards.b.BilbosRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bilbo, Retired Burglar", 196, Rarity.UNCOMMON, mage.cards.b.BilboRetiredBurglar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bilbo, Retired Burglar", 403, Rarity.UNCOMMON, mage.cards.b.BilboRetiredBurglar.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Bill Ferny, Bree Swindler", 42, Rarity.UNCOMMON, mage.cards.b.BillFernyBreeSwindler.class)); - cards.add(new SetCardInfo("Bill the Pony", 3, Rarity.UNCOMMON, mage.cards.b.BillThePony.class)); - cards.add(new SetCardInfo("Birthday Escape", 43, Rarity.COMMON, mage.cards.b.BirthdayEscape.class)); - cards.add(new SetCardInfo("Bitter Downfall", 77, Rarity.UNCOMMON, mage.cards.b.BitterDownfall.class)); - cards.add(new SetCardInfo("Bombadil's Song", 154, Rarity.COMMON, mage.cards.b.BombadilsSong.class)); - cards.add(new SetCardInfo("Book of Mazarbul", 116, Rarity.UNCOMMON, mage.cards.b.BookOfMazarbul.class)); + cards.add(new SetCardInfo("Bilbo, Retired Burglar", 647, Rarity.UNCOMMON, mage.cards.b.BilboRetiredBurglar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bill Ferny, Bree Swindler", 42, Rarity.UNCOMMON, mage.cards.b.BillFernyBreeSwindler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bill Ferny, Bree Swindler", 493, Rarity.UNCOMMON, mage.cards.b.BillFernyBreeSwindler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bill the Pony", 3, Rarity.UNCOMMON, mage.cards.b.BillThePony.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bill the Pony", 454, Rarity.UNCOMMON, mage.cards.b.BillThePony.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Birthday Escape", 43, Rarity.COMMON, mage.cards.b.BirthdayEscape.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Birthday Escape", 494, Rarity.COMMON, mage.cards.b.BirthdayEscape.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bitter Downfall", 528, Rarity.UNCOMMON, mage.cards.b.BitterDownfall.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bitter Downfall", 77, Rarity.UNCOMMON, mage.cards.b.BitterDownfall.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bombadil's Song", 154, Rarity.COMMON, mage.cards.b.BombadilsSong.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bombadil's Song", 605, Rarity.COMMON, mage.cards.b.BombadilsSong.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Book of Mazarbul", 116, Rarity.UNCOMMON, mage.cards.b.BookOfMazarbul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Book of Mazarbul", 567, Rarity.UNCOMMON, mage.cards.b.BookOfMazarbul.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Borne Upon a Wind", 350, Rarity.RARE, mage.cards.b.BorneUponAWind.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Borne Upon a Wind", 44, Rarity.RARE, mage.cards.b.BorneUponAWind.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Borne Upon a Wind", 495, Rarity.RARE, mage.cards.b.BorneUponAWind.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Borne Upon a Wind", 761, Rarity.RARE, mage.cards.b.BorneUponAWind.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Boromir, Warden of the Tower", 302, Rarity.RARE, mage.cards.b.BoromirWardenOfTheTower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Boromir, Warden of the Tower", 4, Rarity.RARE, mage.cards.b.BoromirWardenOfTheTower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Boromir, Warden of the Tower", 407, Rarity.RARE, mage.cards.b.BoromirWardenOfTheTower.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Brandywine Farmer", 155, Rarity.COMMON, mage.cards.b.BrandywineFarmer.class)); - cards.add(new SetCardInfo("Breaking of the Fellowship", 117, Rarity.COMMON, mage.cards.b.BreakingOfTheFellowship.class)); - cards.add(new SetCardInfo("Butterbur, Bree Innkeeper", 197, Rarity.UNCOMMON, mage.cards.b.ButterburBreeInnkeeper.class)); + cards.add(new SetCardInfo("Boromir, Warden of the Tower", 455, Rarity.RARE, mage.cards.b.BoromirWardenOfTheTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Boromir, Warden of the Tower", 794, Rarity.RARE, mage.cards.b.BoromirWardenOfTheTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Brandywine Farmer", 155, Rarity.COMMON, mage.cards.b.BrandywineFarmer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Brandywine Farmer", 606, Rarity.COMMON, mage.cards.b.BrandywineFarmer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Breaking of the Fellowship", 117, Rarity.COMMON, mage.cards.b.BreakingOfTheFellowship.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Breaking of the Fellowship", 568, Rarity.COMMON, mage.cards.b.BreakingOfTheFellowship.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Butterbur, Bree Innkeeper", 197, Rarity.UNCOMMON, mage.cards.b.ButterburBreeInnkeeper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Butterbur, Bree Innkeeper", 648, Rarity.UNCOMMON, mage.cards.b.ButterburBreeInnkeeper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Call of the Ring", 355, Rarity.RARE, mage.cards.c.CallOfTheRing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Call of the Ring", 530, Rarity.RARE, mage.cards.c.CallOfTheRing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Call of the Ring", 766, Rarity.RARE, mage.cards.c.CallOfTheRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Call of the Ring", 79, Rarity.RARE, mage.cards.c.CallOfTheRing.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Captain of Umbar", 45, Rarity.COMMON, mage.cards.c.CaptainOfUmbar.class)); - cards.add(new SetCardInfo("Cast into the Fire", 118, Rarity.COMMON, mage.cards.c.CastIntoTheFire.class)); - cards.add(new SetCardInfo("Celeborn the Wise", 156, Rarity.UNCOMMON, mage.cards.c.CelebornTheWise.class)); - cards.add(new SetCardInfo("Chance-Met Elves", 157, Rarity.COMMON, mage.cards.c.ChanceMetElves.class)); - cards.add(new SetCardInfo("Cirith Ungol Patrol", 80, Rarity.COMMON, mage.cards.c.CirithUngolPatrol.class)); - cards.add(new SetCardInfo("Claim the Precious", 81, Rarity.COMMON, mage.cards.c.ClaimThePrecious.class)); - cards.add(new SetCardInfo("Council's Deliberation", 46, Rarity.UNCOMMON, mage.cards.c.CouncilsDeliberation.class)); + cards.add(new SetCardInfo("Captain of Umbar", 45, Rarity.COMMON, mage.cards.c.CaptainOfUmbar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Captain of Umbar", 496, Rarity.COMMON, mage.cards.c.CaptainOfUmbar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cast into the Fire", 118, Rarity.COMMON, mage.cards.c.CastIntoTheFire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cast into the Fire", 569, Rarity.COMMON, mage.cards.c.CastIntoTheFire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Celeborn the Wise", 156, Rarity.UNCOMMON, mage.cards.c.CelebornTheWise.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Celeborn the Wise", 607, Rarity.UNCOMMON, mage.cards.c.CelebornTheWise.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chance-Met Elves", 157, Rarity.COMMON, mage.cards.c.ChanceMetElves.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chance-Met Elves", 608, Rarity.COMMON, mage.cards.c.ChanceMetElves.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cirith Ungol Patrol", 531, Rarity.COMMON, mage.cards.c.CirithUngolPatrol.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cirith Ungol Patrol", 80, Rarity.COMMON, mage.cards.c.CirithUngolPatrol.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Claim the Precious", 532, Rarity.COMMON, mage.cards.c.ClaimThePrecious.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Claim the Precious", 81, Rarity.COMMON, mage.cards.c.ClaimThePrecious.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Council's Deliberation", 46, Rarity.UNCOMMON, mage.cards.c.CouncilsDeliberation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Council's Deliberation", 497, Rarity.UNCOMMON, mage.cards.c.CouncilsDeliberation.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dawn of a New Age", 347, Rarity.MYTHIC, mage.cards.d.DawnOfANewAge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dawn of a New Age", 456, Rarity.MYTHIC, mage.cards.d.DawnOfANewAge.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dawn of a New Age", 5, Rarity.MYTHIC, mage.cards.d.DawnOfANewAge.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Deceive the Messenger", 47, Rarity.COMMON, mage.cards.d.DeceiveTheMessenger.class)); + cards.add(new SetCardInfo("Dawn of a New Age", 731, Rarity.MYTHIC, mage.cards.d.DawnOfANewAge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dawn of a New Age", 758, Rarity.MYTHIC, mage.cards.d.DawnOfANewAge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Deceive the Messenger", 47, Rarity.COMMON, mage.cards.d.DeceiveTheMessenger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Deceive the Messenger", 498, Rarity.COMMON, mage.cards.d.DeceiveTheMessenger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Delighted Halfling", 158, Rarity.RARE, mage.cards.d.DelightedHalfling.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Delighted Halfling", 363, Rarity.RARE, mage.cards.d.DelightedHalfling.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Delighted Halfling", 402, Rarity.RARE, mage.cards.d.DelightedHalfling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Delighted Halfling", 609, Rarity.RARE, mage.cards.d.DelightedHalfling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Delighted Halfling", 774, Rarity.RARE, mage.cards.d.DelightedHalfling.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Denethor, Ruling Steward", 198, Rarity.UNCOMMON, mage.cards.d.DenethorRulingSteward.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Denethor, Ruling Steward", 649, Rarity.UNCOMMON, mage.cards.d.DenethorRulingSteward.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Display of Power", 119, Rarity.RARE, mage.cards.d.DisplayOfPower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Display of Power", 358, Rarity.RARE, mage.cards.d.DisplayOfPower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Display of Power", 570, Rarity.RARE, mage.cards.d.DisplayOfPower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Display of Power", 769, Rarity.RARE, mage.cards.d.DisplayOfPower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Doors of Durin", 199, Rarity.RARE, mage.cards.d.DoorsOfDurin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Doors of Durin", 368, Rarity.RARE, mage.cards.d.DoorsOfDurin.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Dreadful as the Storm", 48, Rarity.COMMON, mage.cards.d.DreadfulAsTheStorm.class)); - cards.add(new SetCardInfo("Dunedain Blade", 6, Rarity.COMMON, mage.cards.d.DunedainBlade.class)); - cards.add(new SetCardInfo("Dunedain Rangers", 159, Rarity.UNCOMMON, mage.cards.d.DunedainRangers.class)); + cards.add(new SetCardInfo("Doors of Durin", 650, Rarity.RARE, mage.cards.d.DoorsOfDurin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Doors of Durin", 779, Rarity.RARE, mage.cards.d.DoorsOfDurin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dreadful as the Storm", 48, Rarity.COMMON, mage.cards.d.DreadfulAsTheStorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dreadful as the Storm", 499, Rarity.COMMON, mage.cards.d.DreadfulAsTheStorm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dunedain Blade", 457, Rarity.COMMON, mage.cards.d.DunedainBlade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dunedain Blade", 6, Rarity.COMMON, mage.cards.d.DunedainBlade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dunedain Rangers", 159, Rarity.UNCOMMON, mage.cards.d.DunedainRangers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dunedain Rangers", 610, Rarity.UNCOMMON, mage.cards.d.DunedainRangers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dunland Crebain", 411, Rarity.COMMON, mage.cards.d.DunlandCrebain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dunland Crebain", 533, Rarity.COMMON, mage.cards.d.DunlandCrebain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dunland Crebain", 82, Rarity.COMMON, mage.cards.d.DunlandCrebain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Eagles of the North", 7, Rarity.COMMON, mage.cards.e.EaglesOfTheNorth.class)); - cards.add(new SetCardInfo("East-Mark Cavalier", 9, Rarity.COMMON, mage.cards.e.EastMarkCavalier.class)); - cards.add(new SetCardInfo("Easterling Vanguard", 83, Rarity.COMMON, mage.cards.e.EasterlingVanguard.class)); - cards.add(new SetCardInfo("Eastfarthing Farmer", 8, Rarity.COMMON, mage.cards.e.EastfarthingFarmer.class)); + cards.add(new SetCardInfo("Eagle of Deliverance", 824, Rarity.RARE, mage.cards.e.EagleOfDeliverance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eagle of Deliverance", 829, Rarity.RARE, mage.cards.e.EagleOfDeliverance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eagles of the North", 458, Rarity.COMMON, mage.cards.e.EaglesOfTheNorth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eagles of the North", 7, Rarity.COMMON, mage.cards.e.EaglesOfTheNorth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("East-Mark Cavalier", 460, Rarity.COMMON, mage.cards.e.EastMarkCavalier.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("East-Mark Cavalier", 9, Rarity.COMMON, mage.cards.e.EastMarkCavalier.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Easterling Vanguard", 534, Rarity.COMMON, mage.cards.e.EasterlingVanguard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Easterling Vanguard", 83, Rarity.COMMON, mage.cards.e.EasterlingVanguard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eastfarthing Farmer", 459, Rarity.COMMON, mage.cards.e.EastfarthingFarmer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eastfarthing Farmer", 8, Rarity.COMMON, mage.cards.e.EastfarthingFarmer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elanor Gardner", 286, Rarity.RARE, mage.cards.e.ElanorGardner.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elanor Gardner", 387, Rarity.RARE, mage.cards.e.ElanorGardner.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elrond, Lord of Rivendell", 307, Rarity.UNCOMMON, mage.cards.e.ElrondLordOfRivendell.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elrond, Lord of Rivendell", 49, Rarity.UNCOMMON, mage.cards.e.ElrondLordOfRivendell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elrond, Lord of Rivendell", 500, Rarity.UNCOMMON, mage.cards.e.ElrondLordOfRivendell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elrond, Lord of Rivendell", 799, Rarity.UNCOMMON, mage.cards.e.ElrondLordOfRivendell.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elrond, Master of Healing", 200, Rarity.RARE, mage.cards.e.ElrondMasterOfHealing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elrond, Master of Healing", 318, Rarity.RARE, mage.cards.e.ElrondMasterOfHealing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elrond, Master of Healing", 447, Rarity.RARE, mage.cards.e.ElrondMasterOfHealing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elrond, Master of Healing", 651, Rarity.RARE, mage.cards.e.ElrondMasterOfHealing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elrond, Master of Healing", 810, Rarity.RARE, mage.cards.e.ElrondMasterOfHealing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elven Chorus", 160, Rarity.RARE, mage.cards.e.ElvenChorus.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elven Chorus", 364, Rarity.RARE, mage.cards.e.ElvenChorus.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Elven Farsight", 161, Rarity.COMMON, mage.cards.e.ElvenFarsight.class)); + cards.add(new SetCardInfo("Elven Chorus", 611, Rarity.RARE, mage.cards.e.ElvenChorus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elven Chorus", 775, Rarity.RARE, mage.cards.e.ElvenChorus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elven Farsight", 161, Rarity.COMMON, mage.cards.e.ElvenFarsight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elven Farsight", 612, Rarity.COMMON, mage.cards.e.ElvenFarsight.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elvish Mariner", 283, Rarity.RARE, mage.cards.e.ElvishMariner.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elvish Mariner", 384, Rarity.RARE, mage.cards.e.ElvishMariner.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Enraged Huorn", 162, Rarity.COMMON, mage.cards.e.EnragedHuorn.class)); - cards.add(new SetCardInfo("Ent's Fury", 164, Rarity.COMMON, mage.cards.e.EntsFury.class)); - cards.add(new SetCardInfo("Ent-Draught Basin", 238, Rarity.UNCOMMON, mage.cards.e.EntDraughtBasin.class)); - cards.add(new SetCardInfo("Entish Restoration", 163, Rarity.UNCOMMON, mage.cards.e.EntishRestoration.class)); - cards.add(new SetCardInfo("Eomer of the Riddermark", 121, Rarity.UNCOMMON, mage.cards.e.EomerOfTheRiddermark.class)); + cards.add(new SetCardInfo("Enraged Huorn", 162, Rarity.COMMON, mage.cards.e.EnragedHuorn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Enraged Huorn", 613, Rarity.COMMON, mage.cards.e.EnragedHuorn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ent's Fury", 164, Rarity.COMMON, mage.cards.e.EntsFury.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ent's Fury", 615, Rarity.COMMON, mage.cards.e.EntsFury.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ent-Draught Basin", 238, Rarity.UNCOMMON, mage.cards.e.EntDraughtBasin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ent-Draught Basin", 689, Rarity.UNCOMMON, mage.cards.e.EntDraughtBasin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Entish Restoration", 163, Rarity.UNCOMMON, mage.cards.e.EntishRestoration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Entish Restoration", 614, Rarity.UNCOMMON, mage.cards.e.EntishRestoration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eomer of the Riddermark", 121, Rarity.UNCOMMON, mage.cards.e.EomerOfTheRiddermark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eomer of the Riddermark", 572, Rarity.UNCOMMON, mage.cards.e.EomerOfTheRiddermark.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Eomer, Marshal of Rohan", 120, Rarity.RARE, mage.cards.e.EomerMarshalOfRohan.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Eomer, Marshal of Rohan", 428, Rarity.RARE, mage.cards.e.EomerMarshalOfRohan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eomer, Marshal of Rohan", 571, Rarity.RARE, mage.cards.e.EomerMarshalOfRohan.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Eowyn, Fearless Knight", 201, Rarity.RARE, mage.cards.e.EowynFearlessKnight.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Eowyn, Fearless Knight", 430, Rarity.RARE, mage.cards.e.EowynFearlessKnight.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Eowyn, Lady of Rohan", 10, Rarity.UNCOMMON, mage.cards.e.EowynLadyOfRohan.class)); - cards.add(new SetCardInfo("Erebor Flamesmith", 122, Rarity.COMMON, mage.cards.e.EreborFlamesmith.class)); - cards.add(new SetCardInfo("Erkenbrand, Lord of Westfold", 123, Rarity.UNCOMMON, mage.cards.e.ErkenbrandLordOfWestfold.class)); - cards.add(new SetCardInfo("Errand-Rider of Gondor", 11, Rarity.COMMON, mage.cards.e.ErrandRiderOfGondor.class)); - cards.add(new SetCardInfo("Escape from Orthanc", 12, Rarity.COMMON, mage.cards.e.EscapeFromOrthanc.class)); - cards.add(new SetCardInfo("Esquire of the King", 13, Rarity.COMMON, mage.cards.e.EsquireOfTheKing.class)); + cards.add(new SetCardInfo("Eowyn, Fearless Knight", 652, Rarity.RARE, mage.cards.e.EowynFearlessKnight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eowyn, Lady of Rohan", 10, Rarity.UNCOMMON, mage.cards.e.EowynLadyOfRohan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eowyn, Lady of Rohan", 461, Rarity.UNCOMMON, mage.cards.e.EowynLadyOfRohan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Erebor Flamesmith", 122, Rarity.COMMON, mage.cards.e.EreborFlamesmith.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Erebor Flamesmith", 573, Rarity.COMMON, mage.cards.e.EreborFlamesmith.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Erkenbrand, Lord of Westfold", 123, Rarity.UNCOMMON, mage.cards.e.ErkenbrandLordOfWestfold.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Erkenbrand, Lord of Westfold", 574, Rarity.UNCOMMON, mage.cards.e.ErkenbrandLordOfWestfold.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Errand-Rider of Gondor", 11, Rarity.COMMON, mage.cards.e.ErrandRiderOfGondor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Errand-Rider of Gondor", 462, Rarity.COMMON, mage.cards.e.ErrandRiderOfGondor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Escape from Orthanc", 12, Rarity.COMMON, mage.cards.e.EscapeFromOrthanc.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Escape from Orthanc", 463, Rarity.COMMON, mage.cards.e.EscapeFromOrthanc.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Esquire of the King", 13, Rarity.COMMON, mage.cards.e.EsquireOfTheKing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Esquire of the King", 464, Rarity.COMMON, mage.cards.e.EsquireOfTheKing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fall of Cair Andros", 124, Rarity.RARE, mage.cards.f.FallOfCairAndros.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fall of Cair Andros", 359, Rarity.RARE, mage.cards.f.FallOfCairAndros.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fall of Cair Andros", 575, Rarity.RARE, mage.cards.f.FallOfCairAndros.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fall of Cair Andros", 770, Rarity.RARE, mage.cards.f.FallOfCairAndros.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fall of Gil-galad", 165, Rarity.RARE, mage.cards.f.FallOfGilGalad.class)); + cards.add(new SetCardInfo("Fall of Gil-galad", 165, Rarity.RARE, mage.cards.f.FallOfGilGalad.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fall of Gil-galad", 616, Rarity.RARE, mage.cards.f.FallOfGilGalad.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fangorn, Tree Shepherd", 166, Rarity.RARE, mage.cards.f.FangornTreeShepherd.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fangorn, Tree Shepherd", 415, Rarity.RARE, mage.cards.f.FangornTreeShepherd.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fangorn, Tree Shepherd", 617, Rarity.RARE, mage.cards.f.FangornTreeShepherd.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Faramir, Field Commander", 14, Rarity.UNCOMMON, mage.cards.f.FaramirFieldCommander.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Faramir, Field Commander", 303, Rarity.UNCOMMON, mage.cards.f.FaramirFieldCommander.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Faramir, Field Commander", 465, Rarity.UNCOMMON, mage.cards.f.FaramirFieldCommander.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Faramir, Field Commander", 795, Rarity.UNCOMMON, mage.cards.f.FaramirFieldCommander.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Faramir, Prince of Ithilien", 202, Rarity.RARE, mage.cards.f.FaramirPrinceOfIthilien.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Faramir, Prince of Ithilien", 319, Rarity.RARE, mage.cards.f.FaramirPrinceOfIthilien.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fear, Fire, Foes!", 125, Rarity.UNCOMMON, mage.cards.f.FearFireFoes.class)); - cards.add(new SetCardInfo("Fiery Inscription", 126, Rarity.UNCOMMON, mage.cards.f.FieryInscription.class)); - cards.add(new SetCardInfo("Fire of Orthanc", 127, Rarity.COMMON, mage.cards.f.FireOfOrthanc.class)); + cards.add(new SetCardInfo("Faramir, Prince of Ithilien", 653, Rarity.RARE, mage.cards.f.FaramirPrinceOfIthilien.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Faramir, Prince of Ithilien", 811, Rarity.RARE, mage.cards.f.FaramirPrinceOfIthilien.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fear, Fire, Foes!", 125, Rarity.UNCOMMON, mage.cards.f.FearFireFoes.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fear, Fire, Foes!", 576, Rarity.UNCOMMON, mage.cards.f.FearFireFoes.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fiery Inscription", 126, Rarity.UNCOMMON, mage.cards.f.FieryInscription.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fiery Inscription", 577, Rarity.UNCOMMON, mage.cards.f.FieryInscription.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fire of Orthanc", 127, Rarity.COMMON, mage.cards.f.FireOfOrthanc.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fire of Orthanc", 578, Rarity.COMMON, mage.cards.f.FireOfOrthanc.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fires of Mount Doom", 294, Rarity.RARE, mage.cards.f.FiresOfMountDoom.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fires of Mount Doom", 392, Rarity.RARE, mage.cards.f.FiresOfMountDoom.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Flame of Anor", 203, Rarity.RARE, mage.cards.f.FlameOfAnor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Flame of Anor", 406, Rarity.RARE, mage.cards.f.FlameOfAnor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Flame of Anor", 654, Rarity.RARE, mage.cards.f.FlameOfAnor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Flowering of the White Tree", 15, Rarity.RARE, mage.cards.f.FloweringOfTheWhiteTree.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Flowering of the White Tree", 348, Rarity.RARE, mage.cards.f.FloweringOfTheWhiteTree.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fog on the Barrow-Downs", 16, Rarity.COMMON, mage.cards.f.FogOnTheBarrowDowns.class)); + cards.add(new SetCardInfo("Flowering of the White Tree", 466, Rarity.RARE, mage.cards.f.FloweringOfTheWhiteTree.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Flowering of the White Tree", 759, Rarity.RARE, mage.cards.f.FloweringOfTheWhiteTree.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fog on the Barrow-Downs", 16, Rarity.COMMON, mage.cards.f.FogOnTheBarrowDowns.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fog on the Barrow-Downs", 467, Rarity.COMMON, mage.cards.f.FogOnTheBarrowDowns.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Foray of Orcs", 128, Rarity.UNCOMMON, mage.cards.f.ForayOfOrcs.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Foray of Orcs", 417, Rarity.UNCOMMON, mage.cards.f.ForayOfOrcs.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Foray of Orcs", 579, Rarity.UNCOMMON, mage.cards.f.ForayOfOrcs.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 270, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 271, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 280, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Forest", 281, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 721, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 722, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Forge Anew", 17, Rarity.RARE, mage.cards.f.ForgeAnew.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forge Anew", 349, Rarity.RARE, mage.cards.f.ForgeAnew.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forge Anew", 468, Rarity.RARE, mage.cards.f.ForgeAnew.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forge Anew", 760, Rarity.RARE, mage.cards.f.ForgeAnew.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Friendly Rivalry", 204, Rarity.UNCOMMON, mage.cards.f.FriendlyRivalry.class)); + cards.add(new SetCardInfo("Friendly Rivalry", 204, Rarity.UNCOMMON, mage.cards.f.FriendlyRivalry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Friendly Rivalry", 655, Rarity.UNCOMMON, mage.cards.f.FriendlyRivalry.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Frodo Baggins", 205, Rarity.UNCOMMON, mage.cards.f.FrodoBaggins.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Frodo Baggins", 320, Rarity.UNCOMMON, mage.cards.f.FrodoBaggins.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Frodo Baggins", 404, Rarity.UNCOMMON, mage.cards.f.FrodoBaggins.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frodo Baggins", 656, Rarity.UNCOMMON, mage.cards.f.FrodoBaggins.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frodo Baggins", 812, Rarity.UNCOMMON, mage.cards.f.FrodoBaggins.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Frodo, Determined Hero", 289, Rarity.RARE, mage.cards.f.FrodoDeterminedHero.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Frodo, Determined Hero", 388, Rarity.RARE, mage.cards.f.FrodoDeterminedHero.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Frodo, Sauron's Bane", 18, Rarity.RARE, mage.cards.f.FrodoSauronsBane.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Frodo, Sauron's Bane", 304, Rarity.RARE, mage.cards.f.FrodoSauronsBane.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Frodo, Sauron's Bane", 448, Rarity.RARE, mage.cards.f.FrodoSauronsBane.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Galadhrim Bow", 167, Rarity.COMMON, mage.cards.g.GaladhrimBow.class)); - cards.add(new SetCardInfo("Galadhrim Guide", 168, Rarity.COMMON, mage.cards.g.GaladhrimGuide.class)); + cards.add(new SetCardInfo("Frodo, Sauron's Bane", 469, Rarity.RARE, mage.cards.f.FrodoSauronsBane.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frodo, Sauron's Bane", 796, Rarity.RARE, mage.cards.f.FrodoSauronsBane.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Galadhrim Bow", 167, Rarity.COMMON, mage.cards.g.GaladhrimBow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Galadhrim Bow", 618, Rarity.COMMON, mage.cards.g.GaladhrimBow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Galadhrim Guide", 168, Rarity.COMMON, mage.cards.g.GaladhrimGuide.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Galadhrim Guide", 619, Rarity.COMMON, mage.cards.g.GaladhrimGuide.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Galadriel of Lothlorien", 206, Rarity.RARE, mage.cards.g.GaladrielOfLothlorien.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Galadriel of Lothlorien", 321, Rarity.RARE, mage.cards.g.GaladrielOfLothlorien.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Galadriel of Lothlorien", 446, Rarity.RARE, mage.cards.g.GaladrielOfLothlorien.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Galadriel of Lothlorien", 657, Rarity.RARE, mage.cards.g.GaladrielOfLothlorien.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Galadriel of Lothlorien", 813, Rarity.RARE, mage.cards.g.GaladrielOfLothlorien.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Galadriel, Gift-Giver", 296, Rarity.RARE, mage.cards.g.GaladrielGiftGiver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Galadriel, Gift-Giver", 393, Rarity.RARE, mage.cards.g.GaladrielGiftGiver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf the Grey", 207, Rarity.RARE, mage.cards.g.GandalfTheGrey.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf the Grey", 322, Rarity.RARE, mage.cards.g.GandalfTheGrey.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gandalf the Grey", 658, Rarity.RARE, mage.cards.g.GandalfTheGrey.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gandalf the Grey", 814, Rarity.RARE, mage.cards.g.GandalfTheGrey.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf the White", 19, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf the White", 299, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf the White", 305, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf the White", 442, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gandalf's Sanction", 208, Rarity.UNCOMMON, mage.cards.g.GandalfsSanction.class)); + cards.add(new SetCardInfo("Gandalf the White", 470, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gandalf the White", 732, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gandalf the White", 797, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gandalf's Sanction", 208, Rarity.UNCOMMON, mage.cards.g.GandalfsSanction.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gandalf's Sanction", 659, Rarity.UNCOMMON, mage.cards.g.GandalfsSanction.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf, Friend of the Shire", 308, Rarity.UNCOMMON, mage.cards.g.GandalfFriendOfTheShire.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf, Friend of the Shire", 401, Rarity.UNCOMMON, mage.cards.g.GandalfFriendOfTheShire.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf, Friend of the Shire", 50, Rarity.UNCOMMON, mage.cards.g.GandalfFriendOfTheShire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gandalf, Friend of the Shire", 501, Rarity.UNCOMMON, mage.cards.g.GandalfFriendOfTheShire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gandalf, Friend of the Shire", 800, Rarity.UNCOMMON, mage.cards.g.GandalfFriendOfTheShire.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf, White Rider", 290, Rarity.RARE, mage.cards.g.GandalfWhiteRider.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf, White Rider", 389, Rarity.RARE, mage.cards.g.GandalfWhiteRider.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Generous Ent", 169, Rarity.COMMON, mage.cards.g.GenerousEnt.class)); - cards.add(new SetCardInfo("Gift of Strands", 170, Rarity.UNCOMMON, mage.cards.g.GiftOfStrands.class)); - cards.add(new SetCardInfo("Gimli's Axe", 130, Rarity.COMMON, mage.cards.g.GimlisAxe.class)); - cards.add(new SetCardInfo("Gimli's Fury", 131, Rarity.COMMON, mage.cards.g.GimlisFury.class)); + cards.add(new SetCardInfo("Generous Ent", 169, Rarity.COMMON, mage.cards.g.GenerousEnt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Generous Ent", 620, Rarity.COMMON, mage.cards.g.GenerousEnt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gift of Strands", 170, Rarity.UNCOMMON, mage.cards.g.GiftOfStrands.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gift of Strands", 621, Rarity.UNCOMMON, mage.cards.g.GiftOfStrands.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gimli's Axe", 130, Rarity.COMMON, mage.cards.g.GimlisAxe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gimli's Axe", 581, Rarity.COMMON, mage.cards.g.GimlisAxe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gimli's Fury", 131, Rarity.COMMON, mage.cards.g.GimlisFury.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gimli's Fury", 582, Rarity.COMMON, mage.cards.g.GimlisFury.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gimli, Counter of Kills", 129, Rarity.UNCOMMON, mage.cards.g.GimliCounterOfKills.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gimli, Counter of Kills", 312, Rarity.UNCOMMON, mage.cards.g.GimliCounterOfKills.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gimli, Counter of Kills", 580, Rarity.UNCOMMON, mage.cards.g.GimliCounterOfKills.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gimli, Counter of Kills", 804, Rarity.UNCOMMON, mage.cards.g.GimliCounterOfKills.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gimli, Mournful Avenger", 209, Rarity.RARE, mage.cards.g.GimliMournfulAvenger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gimli, Mournful Avenger", 323, Rarity.RARE, mage.cards.g.GimliMournfulAvenger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gimli, Mournful Avenger", 436, Rarity.RARE, mage.cards.g.GimliMournfulAvenger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gimli, Mournful Avenger", 660, Rarity.RARE, mage.cards.g.GimliMournfulAvenger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gimli, Mournful Avenger", 815, Rarity.RARE, mage.cards.g.GimliMournfulAvenger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Glamdring", 239, Rarity.MYTHIC, mage.cards.g.Glamdring.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Glamdring", 376, Rarity.MYTHIC, mage.cards.g.Glamdring.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Glamdring", 690, Rarity.MYTHIC, mage.cards.g.Glamdring.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Glamdring", 747, Rarity.MYTHIC, mage.cards.g.Glamdring.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Glamdring", 787, Rarity.MYTHIC, mage.cards.g.Glamdring.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gloin, Dwarf Emissary", 132, Rarity.RARE, mage.cards.g.GloinDwarfEmissary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gloin, Dwarf Emissary", 360, Rarity.RARE, mage.cards.g.GloinDwarfEmissary.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Glorfindel, Dauntless Rescuer", 171, Rarity.UNCOMMON, mage.cards.g.GlorfindelDauntlessRescuer.class)); - cards.add(new SetCardInfo("Glorious Gale", 51, Rarity.COMMON, mage.cards.g.GloriousGale.class)); + cards.add(new SetCardInfo("Gloin, Dwarf Emissary", 583, Rarity.RARE, mage.cards.g.GloinDwarfEmissary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gloin, Dwarf Emissary", 771, Rarity.RARE, mage.cards.g.GloinDwarfEmissary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Glorfindel, Dauntless Rescuer", 171, Rarity.UNCOMMON, mage.cards.g.GlorfindelDauntlessRescuer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Glorfindel, Dauntless Rescuer", 622, Rarity.UNCOMMON, mage.cards.g.GlorfindelDauntlessRescuer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Glorious Gale", 502, Rarity.COMMON, mage.cards.g.GloriousGale.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Glorious Gale", 51, Rarity.COMMON, mage.cards.g.GloriousGale.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Goblin Assailant", 295, Rarity.COMMON, mage.cards.g.GoblinAssailant.class)); - cards.add(new SetCardInfo("Goblin Fireleaper", 133, Rarity.UNCOMMON, mage.cards.g.GoblinFireleaper.class)); + cards.add(new SetCardInfo("Goblin Fireleaper", 133, Rarity.UNCOMMON, mage.cards.g.GoblinFireleaper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Goblin Fireleaper", 584, Rarity.UNCOMMON, mage.cards.g.GoblinFireleaper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Goldberry, River-Daughter", 351, Rarity.RARE, mage.cards.g.GoldberryRiverDaughter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Goldberry, River-Daughter", 503, Rarity.RARE, mage.cards.g.GoldberryRiverDaughter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Goldberry, River-Daughter", 52, Rarity.RARE, mage.cards.g.GoldberryRiverDaughter.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gollum's Bite", 85, Rarity.UNCOMMON, mage.cards.g.GollumsBite.class)); + cards.add(new SetCardInfo("Goldberry, River-Daughter", 762, Rarity.RARE, mage.cards.g.GoldberryRiverDaughter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gollum's Bite", 536, Rarity.UNCOMMON, mage.cards.g.GollumsBite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gollum's Bite", 85, Rarity.UNCOMMON, mage.cards.g.GollumsBite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gollum, Patient Plotter", 309, Rarity.UNCOMMON, mage.cards.g.GollumPatientPlotter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gollum, Patient Plotter", 450, Rarity.UNCOMMON, mage.cards.g.GollumPatientPlotter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gollum, Patient Plotter", 535, Rarity.UNCOMMON, mage.cards.g.GollumPatientPlotter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gollum, Patient Plotter", 801, Rarity.UNCOMMON, mage.cards.g.GollumPatientPlotter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gollum, Patient Plotter", 84, Rarity.UNCOMMON, mage.cards.g.GollumPatientPlotter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gollum, Scheming Guide", 292, Rarity.RARE, mage.cards.g.GollumSchemingGuide.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gollum, Scheming Guide", 390, Rarity.RARE, mage.cards.g.GollumSchemingGuide.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorbag of Minas Morgul", 86, Rarity.UNCOMMON, mage.cards.g.GorbagOfMinasMorgul.class)); + cards.add(new SetCardInfo("Gorbag of Minas Morgul", 537, Rarity.UNCOMMON, mage.cards.g.GorbagOfMinasMorgul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorbag of Minas Morgul", 86, Rarity.UNCOMMON, mage.cards.g.GorbagOfMinasMorgul.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gothmog, Morgul Lieutenant", 429, Rarity.UNCOMMON, mage.cards.g.GothmogMorgulLieutenant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gothmog, Morgul Lieutenant", 538, Rarity.UNCOMMON, mage.cards.g.GothmogMorgulLieutenant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gothmog, Morgul Lieutenant", 87, Rarity.UNCOMMON, mage.cards.g.GothmogMorgulLieutenant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Great Hall of the Citadel", 254, Rarity.COMMON, mage.cards.g.GreatHallOfTheCitadel.class)); - cards.add(new SetCardInfo("Grey Havens Navigator", 53, Rarity.COMMON, mage.cards.g.GreyHavensNavigator.class)); - cards.add(new SetCardInfo("Grima Wormtongue", 88, Rarity.UNCOMMON, mage.cards.g.GrimaWormtongue.class)); - cards.add(new SetCardInfo("Grishnakh, Brash Instigator", 134, Rarity.UNCOMMON, mage.cards.g.GrishnakhBrashInstigator.class)); - cards.add(new SetCardInfo("Grond, the Gatebreaker", 89, Rarity.UNCOMMON, mage.cards.g.GrondTheGatebreaker.class)); - cards.add(new SetCardInfo("Gwaihir the Windlord", 210, Rarity.UNCOMMON, mage.cards.g.GwaihirTheWindlord.class)); - cards.add(new SetCardInfo("Haradrim Spearmaster", 135, Rarity.COMMON, mage.cards.h.HaradrimSpearmaster.class)); - cards.add(new SetCardInfo("Haunt of the Dead Marshes", 90, Rarity.COMMON, mage.cards.h.HauntOfTheDeadMarshes.class)); + cards.add(new SetCardInfo("Great Hall of the Citadel", 254, Rarity.COMMON, mage.cards.g.GreatHallOfTheCitadel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Great Hall of the Citadel", 705, Rarity.COMMON, mage.cards.g.GreatHallOfTheCitadel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grey Havens Navigator", 504, Rarity.COMMON, mage.cards.g.GreyHavensNavigator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grey Havens Navigator", 53, Rarity.COMMON, mage.cards.g.GreyHavensNavigator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grima Wormtongue", 539, Rarity.UNCOMMON, mage.cards.g.GrimaWormtongue.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grima Wormtongue", 88, Rarity.UNCOMMON, mage.cards.g.GrimaWormtongue.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grishnakh, Brash Instigator", 134, Rarity.UNCOMMON, mage.cards.g.GrishnakhBrashInstigator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grishnakh, Brash Instigator", 585, Rarity.UNCOMMON, mage.cards.g.GrishnakhBrashInstigator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grond, the Gatebreaker", 540, Rarity.UNCOMMON, mage.cards.g.GrondTheGatebreaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Grond, the Gatebreaker", 89, Rarity.UNCOMMON, mage.cards.g.GrondTheGatebreaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gwaihir the Windlord", 210, Rarity.UNCOMMON, mage.cards.g.GwaihirTheWindlord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gwaihir the Windlord", 661, Rarity.UNCOMMON, mage.cards.g.GwaihirTheWindlord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Haradrim Spearmaster", 135, Rarity.COMMON, mage.cards.h.HaradrimSpearmaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Haradrim Spearmaster", 586, Rarity.COMMON, mage.cards.h.HaradrimSpearmaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Haunt of the Dead Marshes", 541, Rarity.COMMON, mage.cards.h.HauntOfTheDeadMarshes.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Haunt of the Dead Marshes", 90, Rarity.COMMON, mage.cards.h.HauntOfTheDeadMarshes.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hew the Entwood", 136, Rarity.MYTHIC, mage.cards.h.HewTheEntwood.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hew the Entwood", 361, Rarity.MYTHIC, mage.cards.h.HewTheEntwood.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Hithlain Knots", 54, Rarity.COMMON, mage.cards.h.HithlainKnots.class)); - cards.add(new SetCardInfo("Hobbit's Sting", 20, Rarity.COMMON, mage.cards.h.HobbitsSting.class)); + cards.add(new SetCardInfo("Hew the Entwood", 587, Rarity.MYTHIC, mage.cards.h.HewTheEntwood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hew the Entwood", 737, Rarity.MYTHIC, mage.cards.h.HewTheEntwood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hew the Entwood", 772, Rarity.MYTHIC, mage.cards.h.HewTheEntwood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hithlain Knots", 505, Rarity.COMMON, mage.cards.h.HithlainKnots.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hithlain Knots", 54, Rarity.COMMON, mage.cards.h.HithlainKnots.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hobbit's Sting", 20, Rarity.COMMON, mage.cards.h.HobbitsSting.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hobbit's Sting", 471, Rarity.COMMON, mage.cards.h.HobbitsSting.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Horn of Gondor", 240, Rarity.RARE, mage.cards.h.HornOfGondor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Horn of Gondor", 377, Rarity.RARE, mage.cards.h.HornOfGondor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Horn of Gondor", 691, Rarity.RARE, mage.cards.h.HornOfGondor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Horn of Gondor", 788, Rarity.RARE, mage.cards.h.HornOfGondor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Horn of the Mark", 241, Rarity.RARE, mage.cards.h.HornOfTheMark.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Horn of the Mark", 378, Rarity.RARE, mage.cards.h.HornOfTheMark.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Horses of the Bruinen", 55, Rarity.UNCOMMON, mage.cards.h.HorsesOfTheBruinen.class)); - cards.add(new SetCardInfo("Improvised Club", 137, Rarity.COMMON, mage.cards.i.ImprovisedClub.class)); - cards.add(new SetCardInfo("Inherited Envelope", 242, Rarity.COMMON, mage.cards.i.InheritedEnvelope.class)); - cards.add(new SetCardInfo("Ioreth of the Healing House", 56, Rarity.UNCOMMON, mage.cards.i.IorethOfTheHealingHouse.class)); + cards.add(new SetCardInfo("Horn of the Mark", 692, Rarity.RARE, mage.cards.h.HornOfTheMark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Horn of the Mark", 789, Rarity.RARE, mage.cards.h.HornOfTheMark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Horses of the Bruinen", 506, Rarity.UNCOMMON, mage.cards.h.HorsesOfTheBruinen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Horses of the Bruinen", 55, Rarity.UNCOMMON, mage.cards.h.HorsesOfTheBruinen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Improvised Club", 137, Rarity.COMMON, mage.cards.i.ImprovisedClub.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Improvised Club", 588, Rarity.COMMON, mage.cards.i.ImprovisedClub.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inherited Envelope", 242, Rarity.COMMON, mage.cards.i.InheritedEnvelope.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inherited Envelope", 693, Rarity.COMMON, mage.cards.i.InheritedEnvelope.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ioreth of the Healing House", 507, Rarity.UNCOMMON, mage.cards.i.IorethOfTheHealingHouse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ioreth of the Healing House", 56, Rarity.UNCOMMON, mage.cards.i.IorethOfTheHealingHouse.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Isildur's Fateful Strike", 356, Rarity.RARE, mage.cards.i.IsildursFatefulStrike.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Isildur's Fateful Strike", 542, Rarity.RARE, mage.cards.i.IsildursFatefulStrike.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Isildur's Fateful Strike", 767, Rarity.RARE, mage.cards.i.IsildursFatefulStrike.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Isildur's Fateful Strike", 91, Rarity.RARE, mage.cards.i.IsildursFatefulStrike.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 264, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 265, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 274, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Island", 275, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Isolation at Orthanc", 57, Rarity.COMMON, mage.cards.i.IsolationAtOrthanc.class)); - cards.add(new SetCardInfo("Ithilien Kingfisher", 58, Rarity.COMMON, mage.cards.i.IthilienKingfisher.class)); + cards.add(new SetCardInfo("Island", 715, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 716, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Isolation at Orthanc", 508, Rarity.COMMON, mage.cards.i.IsolationAtOrthanc.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Isolation at Orthanc", 57, Rarity.COMMON, mage.cards.i.IsolationAtOrthanc.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ithilien Kingfisher", 509, Rarity.COMMON, mage.cards.i.IthilienKingfisher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ithilien Kingfisher", 58, Rarity.COMMON, mage.cards.i.IthilienKingfisher.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("King of the Oathbreakers", 211, Rarity.RARE, mage.cards.k.KingOfTheOathbreakers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("King of the Oathbreakers", 369, Rarity.RARE, mage.cards.k.KingOfTheOathbreakers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("King of the Oathbreakers", 662, Rarity.RARE, mage.cards.k.KingOfTheOathbreakers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("King of the Oathbreakers", 780, Rarity.RARE, mage.cards.k.KingOfTheOathbreakers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Knight of the Keep", 291, Rarity.COMMON, mage.cards.k.KnightOfTheKeep.class)); cards.add(new SetCardInfo("Knights of Dol Amroth", 432, Rarity.COMMON, mage.cards.k.KnightsOfDolAmroth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Knights of Dol Amroth", 510, Rarity.COMMON, mage.cards.k.KnightsOfDolAmroth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Knights of Dol Amroth", 59, Rarity.COMMON, mage.cards.k.KnightsOfDolAmroth.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Landroval, Horizon Witness", 21, Rarity.UNCOMMON, mage.cards.l.LandrovalHorizonWitness.class)); + cards.add(new SetCardInfo("Landroval, Horizon Witness", 21, Rarity.UNCOMMON, mage.cards.l.LandrovalHorizonWitness.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Landroval, Horizon Witness", 472, Rarity.UNCOMMON, mage.cards.l.LandrovalHorizonWitness.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lash of the Balrog", 408, Rarity.COMMON, mage.cards.l.LashOfTheBalrog.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lash of the Balrog", 543, Rarity.COMMON, mage.cards.l.LashOfTheBalrog.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lash of the Balrog", 92, Rarity.COMMON, mage.cards.l.LashOfTheBalrog.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Last March of the Ents", 172, Rarity.MYTHIC, mage.cards.l.LastMarchOfTheEnts.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Last March of the Ents", 418, Rarity.MYTHIC, mage.cards.l.LastMarchOfTheEnts.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Last March of the Ents", 623, Rarity.MYTHIC, mage.cards.l.LastMarchOfTheEnts.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Last March of the Ents", 739, Rarity.MYTHIC, mage.cards.l.LastMarchOfTheEnts.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Legolas, Counter of Kills", 212, Rarity.UNCOMMON, mage.cards.l.LegolasCounterOfKills.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Legolas, Counter of Kills", 324, Rarity.UNCOMMON, mage.cards.l.LegolasCounterOfKills.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Legolas, Counter of Kills", 663, Rarity.UNCOMMON, mage.cards.l.LegolasCounterOfKills.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Legolas, Counter of Kills", 816, Rarity.UNCOMMON, mage.cards.l.LegolasCounterOfKills.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Legolas, Master Archer", 173, Rarity.RARE, mage.cards.l.LegolasMasterArcher.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Legolas, Master Archer", 313, Rarity.RARE, mage.cards.l.LegolasMasterArcher.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Legolas, Master Archer", 435, Rarity.RARE, mage.cards.l.LegolasMasterArcher.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Lembas", 243, Rarity.COMMON, mage.cards.l.Lembas.class)); + cards.add(new SetCardInfo("Legolas, Master Archer", 624, Rarity.RARE, mage.cards.l.LegolasMasterArcher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Legolas, Master Archer", 805, Rarity.RARE, mage.cards.l.LegolasMasterArcher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lembas", 243, Rarity.COMMON, mage.cards.l.Lembas.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lembas", 694, Rarity.COMMON, mage.cards.l.Lembas.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lobelia Sackville-Baggins", 357, Rarity.RARE, mage.cards.l.LobeliaSackvilleBaggins.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lobelia Sackville-Baggins", 399, Rarity.RARE, mage.cards.l.LobeliaSackvilleBaggins.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lobelia Sackville-Baggins", 544, Rarity.RARE, mage.cards.l.LobeliaSackvilleBaggins.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lobelia Sackville-Baggins", 768, Rarity.RARE, mage.cards.l.LobeliaSackvilleBaggins.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lobelia Sackville-Baggins", 93, Rarity.RARE, mage.cards.l.LobeliaSackvilleBaggins.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Long List of the Ents", 174, Rarity.UNCOMMON, mage.cards.l.LongListOfTheEnts.class)); - cards.add(new SetCardInfo("Lorien Revealed", 60, Rarity.COMMON, mage.cards.l.LorienRevealed.class)); + cards.add(new SetCardInfo("Long List of the Ents", 174, Rarity.UNCOMMON, mage.cards.l.LongListOfTheEnts.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Long List of the Ents", 625, Rarity.UNCOMMON, mage.cards.l.LongListOfTheEnts.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lorien Revealed", 511, Rarity.COMMON, mage.cards.l.LorienRevealed.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lorien Revealed", 60, Rarity.COMMON, mage.cards.l.LorienRevealed.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lost Isle Calling", 444, Rarity.RARE, mage.cards.l.LostIsleCalling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lost Isle Calling", 512, Rarity.RARE, mage.cards.l.LostIsleCalling.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lost Isle Calling", 61, Rarity.RARE, mage.cards.l.LostIsleCalling.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Lost to Legend", 22, Rarity.UNCOMMON, mage.cards.l.LostToLegend.class)); - cards.add(new SetCardInfo("Lothlorien Lookout", 175, Rarity.COMMON, mage.cards.l.LothlorienLookout.class)); + cards.add(new SetCardInfo("Lost to Legend", 22, Rarity.UNCOMMON, mage.cards.l.LostToLegend.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lost to Legend", 473, Rarity.UNCOMMON, mage.cards.l.LostToLegend.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lothlorien Lookout", 175, Rarity.COMMON, mage.cards.l.LothlorienLookout.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lothlorien Lookout", 626, Rarity.COMMON, mage.cards.l.LothlorienLookout.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lotho, Corrupt Shirriff", 213, Rarity.RARE, mage.cards.l.LothoCorruptShirriff.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lotho, Corrupt Shirriff", 370, Rarity.RARE, mage.cards.l.LothoCorruptShirriff.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lotho, Corrupt Shirriff", 664, Rarity.RARE, mage.cards.l.LothoCorruptShirriff.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lotho, Corrupt Shirriff", 781, Rarity.RARE, mage.cards.l.LothoCorruptShirriff.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Many Partings", 176, Rarity.COMMON, mage.cards.m.ManyPartings.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Many Partings", 445, Rarity.COMMON, mage.cards.m.ManyPartings.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("March from the Black Gate", 94, Rarity.UNCOMMON, mage.cards.m.MarchFromTheBlackGate.class)); - cards.add(new SetCardInfo("Mauhur, Uruk-hai Captain", 214, Rarity.UNCOMMON, mage.cards.m.MauhurUrukHaiCaptain.class)); - cards.add(new SetCardInfo("Meneldor, Swift Savior", 62, Rarity.UNCOMMON, mage.cards.m.MeneldorSwiftSavior.class)); + cards.add(new SetCardInfo("Many Partings", 627, Rarity.COMMON, mage.cards.m.ManyPartings.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March from the Black Gate", 545, Rarity.UNCOMMON, mage.cards.m.MarchFromTheBlackGate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March from the Black Gate", 94, Rarity.UNCOMMON, mage.cards.m.MarchFromTheBlackGate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mauhur, Uruk-hai Captain", 214, Rarity.UNCOMMON, mage.cards.m.MauhurUrukHaiCaptain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mauhur, Uruk-hai Captain", 665, Rarity.UNCOMMON, mage.cards.m.MauhurUrukHaiCaptain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Meneldor, Swift Savior", 513, Rarity.UNCOMMON, mage.cards.m.MeneldorSwiftSavior.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Meneldor, Swift Savior", 62, Rarity.UNCOMMON, mage.cards.m.MeneldorSwiftSavior.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Meriadoc Brandybuck", 177, Rarity.UNCOMMON, mage.cards.m.MeriadocBrandybuck.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Meriadoc Brandybuck", 314, Rarity.UNCOMMON, mage.cards.m.MeriadocBrandybuck.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Meriadoc Brandybuck", 628, Rarity.UNCOMMON, mage.cards.m.MeriadocBrandybuck.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Meriadoc Brandybuck", 806, Rarity.UNCOMMON, mage.cards.m.MeriadocBrandybuck.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Merry, Esquire of Rohan", 215, Rarity.RARE, mage.cards.m.MerryEsquireOfRohan.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Merry, Esquire of Rohan", 325, Rarity.RARE, mage.cards.m.MerryEsquireOfRohan.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Merry, Esquire of Rohan", 437, Rarity.RARE, mage.cards.m.MerryEsquireOfRohan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Merry, Esquire of Rohan", 666, Rarity.RARE, mage.cards.m.MerryEsquireOfRohan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Merry, Esquire of Rohan", 817, Rarity.RARE, mage.cards.m.MerryEsquireOfRohan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Minas Tirith Garrison", 825, Rarity.RARE, mage.cards.m.MinasTirithGarrison.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Minas Tirith Garrison", 830, Rarity.RARE, mage.cards.m.MinasTirithGarrison.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Minas Tirith", 256, Rarity.RARE, mage.cards.m.MinasTirith.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Minas Tirith", 341, Rarity.RARE, mage.cards.m.MinasTirith.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Minas Tirith", 420, Rarity.RARE, mage.cards.m.MinasTirith.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Minas Tirith", 707, Rarity.RARE, mage.cards.m.MinasTirith.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Minas Tirith", 752, Rarity.RARE, mage.cards.m.MinasTirith.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mines of Moria", 257, Rarity.RARE, mage.cards.m.MinesOfMoria.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mines of Moria", 342, Rarity.RARE, mage.cards.m.MinesOfMoria.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mines of Moria", 708, Rarity.RARE, mage.cards.m.MinesOfMoria.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mines of Moria", 753, Rarity.RARE, mage.cards.m.MinesOfMoria.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mirkwood Bats", 421, Rarity.COMMON, mage.cards.m.MirkwoodBats.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirkwood Bats", 546, Rarity.COMMON, mage.cards.m.MirkwoodBats.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mirkwood Bats", 95, Rarity.COMMON, mage.cards.m.MirkwoodBats.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mirkwood Spider", 178, Rarity.COMMON, mage.cards.m.MirkwoodSpider.class)); - cards.add(new SetCardInfo("Mirror of Galadriel", 244, Rarity.UNCOMMON, mage.cards.m.MirrorOfGaladriel.class)); - cards.add(new SetCardInfo("Mirrormere Guardian", 179, Rarity.COMMON, mage.cards.m.MirrormereGuardian.class)); + cards.add(new SetCardInfo("Mirkwood Channeler", 828, Rarity.RARE, mage.cards.m.MirkwoodChanneler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirkwood Channeler", 833, Rarity.RARE, mage.cards.m.MirkwoodChanneler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirkwood Spider", 178, Rarity.COMMON, mage.cards.m.MirkwoodSpider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirkwood Spider", 629, Rarity.COMMON, mage.cards.m.MirkwoodSpider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirror of Galadriel", 244, Rarity.UNCOMMON, mage.cards.m.MirrorOfGaladriel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirror of Galadriel", 695, Rarity.UNCOMMON, mage.cards.m.MirrorOfGaladriel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirrormere Guardian", 179, Rarity.COMMON, mage.cards.m.MirrormereGuardian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirrormere Guardian", 630, Rarity.COMMON, mage.cards.m.MirrormereGuardian.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mithril Coat", 245, Rarity.RARE, mage.cards.m.MithrilCoat.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mithril Coat", 379, Rarity.RARE, mage.cards.m.MithrilCoat.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mordor Muster", 96, Rarity.COMMON, mage.cards.m.MordorMuster.class)); - cards.add(new SetCardInfo("Mordor Trebuchet", 97, Rarity.COMMON, mage.cards.m.MordorTrebuchet.class)); - cards.add(new SetCardInfo("Morgul-Knife Wound", 98, Rarity.COMMON, mage.cards.m.MorgulKnifeWound.class)); + cards.add(new SetCardInfo("Mithril Coat", 696, Rarity.RARE, mage.cards.m.MithrilCoat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mithril Coat", 790, Rarity.RARE, mage.cards.m.MithrilCoat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mordor Muster", 547, Rarity.COMMON, mage.cards.m.MordorMuster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mordor Muster", 96, Rarity.COMMON, mage.cards.m.MordorMuster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mordor Trebuchet", 548, Rarity.COMMON, mage.cards.m.MordorTrebuchet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mordor Trebuchet", 97, Rarity.COMMON, mage.cards.m.MordorTrebuchet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Morgul-Knife Wound", 549, Rarity.COMMON, mage.cards.m.MorgulKnifeWound.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Morgul-Knife Wound", 98, Rarity.COMMON, mage.cards.m.MorgulKnifeWound.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Moria Marauder", 138, Rarity.RARE, mage.cards.m.MoriaMarauder.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Moria Marauder", 362, Rarity.RARE, mage.cards.m.MoriaMarauder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Moria Marauder", 589, Rarity.RARE, mage.cards.m.MoriaMarauder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Moria Marauder", 773, Rarity.RARE, mage.cards.m.MoriaMarauder.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mount Doom", 258, Rarity.MYTHIC, mage.cards.m.MountDoom.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mount Doom", 343, Rarity.MYTHIC, mage.cards.m.MountDoom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mount Doom", 709, Rarity.MYTHIC, mage.cards.m.MountDoom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mount Doom", 750, Rarity.MYTHIC, mage.cards.m.MountDoom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mount Doom", 754, Rarity.MYTHIC, mage.cards.m.MountDoom.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 268, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 269, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 278, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 279, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Mushroom Watchdogs", 180, Rarity.COMMON, mage.cards.m.MushroomWatchdogs.class)); + cards.add(new SetCardInfo("Mountain", 719, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 720, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mushroom Watchdogs", 180, Rarity.COMMON, mage.cards.m.MushroomWatchdogs.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mushroom Watchdogs", 631, Rarity.COMMON, mage.cards.m.MushroomWatchdogs.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nasty End", 416, Rarity.COMMON, mage.cards.n.NastyEnd.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nasty End", 550, Rarity.COMMON, mage.cards.n.NastyEnd.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nasty End", 99, Rarity.COMMON, mage.cards.n.NastyEnd.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nazgul", 100, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nazgul", 332, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); @@ -312,180 +531,332 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Nazgul", 337, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nazgul", 338, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nazgul", 339, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Nimble Hobbit", 23, Rarity.COMMON, mage.cards.n.NimbleHobbit.class)); - cards.add(new SetCardInfo("Nimrodel Watcher", 63, Rarity.COMMON, mage.cards.n.NimrodelWatcher.class)); - cards.add(new SetCardInfo("Now for Wrath, Now for Ruin!", 24, Rarity.COMMON, mage.cards.n.NowForWrathNowForRuin.class)); - cards.add(new SetCardInfo("Oath of the Grey Host", 101, Rarity.UNCOMMON, mage.cards.o.OathOfTheGreyHost.class)); - cards.add(new SetCardInfo("Old Man Willow", 217, Rarity.UNCOMMON, mage.cards.o.OldManWillow.class)); + cards.add(new SetCardInfo("Nazgul", 551, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nazgul", 723, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nazgul", 724, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nazgul", 725, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nazgul", 726, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nazgul", 727, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nazgul", 728, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nazgul", 729, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nazgul", 730, Rarity.UNCOMMON, mage.cards.n.Nazgul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nimble Hobbit", 23, Rarity.COMMON, mage.cards.n.NimbleHobbit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nimble Hobbit", 474, Rarity.COMMON, mage.cards.n.NimbleHobbit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nimrodel Watcher", 514, Rarity.COMMON, mage.cards.n.NimrodelWatcher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nimrodel Watcher", 63, Rarity.COMMON, mage.cards.n.NimrodelWatcher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Now for Wrath, Now for Ruin!", 24, Rarity.COMMON, mage.cards.n.NowForWrathNowForRuin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Now for Wrath, Now for Ruin!", 475, Rarity.COMMON, mage.cards.n.NowForWrathNowForRuin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Oath of the Grey Host", 101, Rarity.UNCOMMON, mage.cards.o.OathOfTheGreyHost.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Oath of the Grey Host", 552, Rarity.UNCOMMON, mage.cards.o.OathOfTheGreyHost.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Old Man Willow", 217, Rarity.UNCOMMON, mage.cards.o.OldManWillow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Old Man Willow", 668, Rarity.UNCOMMON, mage.cards.o.OldManWillow.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Oliphaunt", 139, Rarity.COMMON, mage.cards.o.Oliphaunt.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Oliphaunt", 426, Rarity.COMMON, mage.cards.o.Oliphaunt.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Olog-hai Crusher", 140, Rarity.COMMON, mage.cards.o.OlogHaiCrusher.class)); - cards.add(new SetCardInfo("One Ring to Rule Them All", 102, Rarity.RARE, mage.cards.o.OneRingToRuleThemAll.class)); + cards.add(new SetCardInfo("Oliphaunt", 590, Rarity.COMMON, mage.cards.o.Oliphaunt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Olog-hai Crusher", 140, Rarity.COMMON, mage.cards.o.OlogHaiCrusher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Olog-hai Crusher", 591, Rarity.COMMON, mage.cards.o.OlogHaiCrusher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("One Ring to Rule Them All", 102, Rarity.RARE, mage.cards.o.OneRingToRuleThemAll.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("One Ring to Rule Them All", 553, Rarity.RARE, mage.cards.o.OneRingToRuleThemAll.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Orcish Bowmasters", 103, Rarity.RARE, mage.cards.o.OrcishBowmasters.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Orcish Bowmasters", 433, Rarity.RARE, mage.cards.o.OrcishBowmasters.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Orcish Medicine", 104, Rarity.COMMON, mage.cards.o.OrcishMedicine.class)); + cards.add(new SetCardInfo("Orcish Bowmasters", 554, Rarity.RARE, mage.cards.o.OrcishBowmasters.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Orcish Medicine", 104, Rarity.COMMON, mage.cards.o.OrcishMedicine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Orcish Medicine", 555, Rarity.COMMON, mage.cards.o.OrcishMedicine.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Palantir of Orthanc", 247, Rarity.MYTHIC, mage.cards.p.PalantirOfOrthanc.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Palantir of Orthanc", 381, Rarity.MYTHIC, mage.cards.p.PalantirOfOrthanc.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Pelargir Survivor", 64, Rarity.COMMON, mage.cards.p.PelargirSurvivor.class)); + cards.add(new SetCardInfo("Palantir of Orthanc", 698, Rarity.MYTHIC, mage.cards.p.PalantirOfOrthanc.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Palantir of Orthanc", 749, Rarity.MYTHIC, mage.cards.p.PalantirOfOrthanc.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Palantir of Orthanc", 792, Rarity.MYTHIC, mage.cards.p.PalantirOfOrthanc.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pelargir Survivor", 515, Rarity.COMMON, mage.cards.p.PelargirSurvivor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pelargir Survivor", 64, Rarity.COMMON, mage.cards.p.PelargirSurvivor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Peregrin Took", 181, Rarity.UNCOMMON, mage.cards.p.PeregrinTook.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Peregrin Took", 315, Rarity.UNCOMMON, mage.cards.p.PeregrinTook.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Peregrin Took", 632, Rarity.UNCOMMON, mage.cards.p.PeregrinTook.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Peregrin Took", 807, Rarity.UNCOMMON, mage.cards.p.PeregrinTook.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Phial of Galadriel", 248, Rarity.RARE, mage.cards.p.PhialOfGaladriel.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Phial of Galadriel", 382, Rarity.RARE, mage.cards.p.PhialOfGaladriel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phial of Galadriel", 699, Rarity.RARE, mage.cards.p.PhialOfGaladriel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phial of Galadriel", 793, Rarity.RARE, mage.cards.p.PhialOfGaladriel.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Pippin's Bravery", 182, Rarity.COMMON, mage.cards.p.PippinsBravery.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Pippin's Bravery", 414, Rarity.COMMON, mage.cards.p.PippinsBravery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pippin's Bravery", 633, Rarity.COMMON, mage.cards.p.PippinsBravery.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Pippin, Guard of the Citadel", 218, Rarity.RARE, mage.cards.p.PippinGuardOfTheCitadel.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Pippin, Guard of the Citadel", 326, Rarity.RARE, mage.cards.p.PippinGuardOfTheCitadel.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Pippin, Guard of the Citadel", 438, Rarity.RARE, mage.cards.p.PippinGuardOfTheCitadel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pippin, Guard of the Citadel", 669, Rarity.RARE, mage.cards.p.PippinGuardOfTheCitadel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pippin, Guard of the Citadel", 818, Rarity.RARE, mage.cards.p.PippinGuardOfTheCitadel.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 262, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 263, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 272, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Plains", 273, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 713, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 714, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Press the Enemy", 352, Rarity.RARE, mage.cards.p.PressTheEnemy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Press the Enemy", 516, Rarity.RARE, mage.cards.p.PressTheEnemy.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Press the Enemy", 65, Rarity.RARE, mage.cards.p.PressTheEnemy.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Press the Enemy", 763, Rarity.RARE, mage.cards.p.PressTheEnemy.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Prince Imrahil the Fair", 219, Rarity.UNCOMMON, mage.cards.p.PrinceImrahilTheFair.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Prince Imrahil the Fair", 431, Rarity.UNCOMMON, mage.cards.p.PrinceImrahilTheFair.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Protector of Gondor", 25, Rarity.COMMON, mage.cards.p.ProtectorOfGondor.class)); - cards.add(new SetCardInfo("Quarrel's End", 141, Rarity.COMMON, mage.cards.q.QuarrelsEnd.class)); + cards.add(new SetCardInfo("Prince Imrahil the Fair", 670, Rarity.UNCOMMON, mage.cards.p.PrinceImrahilTheFair.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Protector of Gondor", 25, Rarity.COMMON, mage.cards.p.ProtectorOfGondor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Protector of Gondor", 476, Rarity.COMMON, mage.cards.p.ProtectorOfGondor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Quarrel's End", 141, Rarity.COMMON, mage.cards.q.QuarrelsEnd.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Quarrel's End", 592, Rarity.COMMON, mage.cards.q.QuarrelsEnd.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Quickbeam, Upstart Ent", 183, Rarity.UNCOMMON, mage.cards.q.QuickbeamUpstartEnt.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Quickbeam, Upstart Ent", 419, Rarity.UNCOMMON, mage.cards.q.QuickbeamUpstartEnt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Quickbeam, Upstart Ent", 634, Rarity.UNCOMMON, mage.cards.q.QuickbeamUpstartEnt.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Radagast the Brown", 184, Rarity.MYTHIC, mage.cards.r.RadagastTheBrown.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Radagast the Brown", 365, Rarity.MYTHIC, mage.cards.r.RadagastTheBrown.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Rally at the Hornburg", 142, Rarity.COMMON, mage.cards.r.RallyAtTheHornburg.class)); - cards.add(new SetCardInfo("Ranger's Firebrand", 143, Rarity.UNCOMMON, mage.cards.r.RangersFirebrand.class)); + cards.add(new SetCardInfo("Radagast the Brown", 635, Rarity.MYTHIC, mage.cards.r.RadagastTheBrown.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Radagast the Brown", 740, Rarity.MYTHIC, mage.cards.r.RadagastTheBrown.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Radagast the Brown", 776, Rarity.MYTHIC, mage.cards.r.RadagastTheBrown.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rally at the Hornburg", 142, Rarity.COMMON, mage.cards.r.RallyAtTheHornburg.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rally at the Hornburg", 593, Rarity.COMMON, mage.cards.r.RallyAtTheHornburg.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ranger's Firebrand", 143, Rarity.UNCOMMON, mage.cards.r.RangersFirebrand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ranger's Firebrand", 594, Rarity.UNCOMMON, mage.cards.r.RangersFirebrand.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rangers of Ithilien", 353, Rarity.RARE, mage.cards.r.RangersOfIthilien.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rangers of Ithilien", 517, Rarity.RARE, mage.cards.r.RangersOfIthilien.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rangers of Ithilien", 66, Rarity.RARE, mage.cards.r.RangersOfIthilien.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rangers of Ithilien", 764, Rarity.RARE, mage.cards.r.RangersOfIthilien.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Relentless Rohirrim", 144, Rarity.COMMON, mage.cards.r.RelentlessRohirrim.class)); - cards.add(new SetCardInfo("Reprieve", 26, Rarity.UNCOMMON, mage.cards.r.Reprieve.class)); - cards.add(new SetCardInfo("Revive the Shire", 185, Rarity.COMMON, mage.cards.r.ReviveTheShire.class)); - cards.add(new SetCardInfo("Ringsight", 220, Rarity.UNCOMMON, mage.cards.r.Ringsight.class)); + cards.add(new SetCardInfo("Relentless Rohirrim", 144, Rarity.COMMON, mage.cards.r.RelentlessRohirrim.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Relentless Rohirrim", 595, Rarity.COMMON, mage.cards.r.RelentlessRohirrim.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reprieve", 26, Rarity.UNCOMMON, mage.cards.r.Reprieve.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reprieve", 477, Rarity.UNCOMMON, mage.cards.r.Reprieve.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Revive the Shire", 185, Rarity.COMMON, mage.cards.r.ReviveTheShire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Revive the Shire", 636, Rarity.COMMON, mage.cards.r.ReviveTheShire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Riders of the Mark", 827, Rarity.RARE, mage.cards.r.RidersOfTheMark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Riders of the Mark", 832, Rarity.RARE, mage.cards.r.RidersOfTheMark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ringsight", 220, Rarity.UNCOMMON, mage.cards.r.Ringsight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ringsight", 671, Rarity.UNCOMMON, mage.cards.r.Ringsight.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ringwraiths", 284, Rarity.RARE, mage.cards.r.Ringwraiths.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ringwraiths", 385, Rarity.RARE, mage.cards.r.Ringwraiths.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Rise of the Witch-king", 221, Rarity.UNCOMMON, mage.cards.r.RiseOfTheWitchKing.class)); + cards.add(new SetCardInfo("Rise of the Witch-king", 221, Rarity.UNCOMMON, mage.cards.r.RiseOfTheWitchKing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rise of the Witch-king", 672, Rarity.UNCOMMON, mage.cards.r.RiseOfTheWitchKing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rising of the Day", 145, Rarity.UNCOMMON, mage.cards.r.RisingOfTheDay.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rising of the Day", 427, Rarity.UNCOMMON, mage.cards.r.RisingOfTheDay.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rising of the Day", 596, Rarity.UNCOMMON, mage.cards.r.RisingOfTheDay.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rivendell", 259, Rarity.RARE, mage.cards.r.Rivendell.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rivendell", 344, Rarity.RARE, mage.cards.r.Rivendell.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Rohirrim Lancer", 146, Rarity.COMMON, mage.cards.r.RohirrimLancer.class)); + cards.add(new SetCardInfo("Rivendell", 710, Rarity.RARE, mage.cards.r.Rivendell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rivendell", 755, Rarity.RARE, mage.cards.r.Rivendell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rohirrim Lancer", 146, Rarity.COMMON, mage.cards.r.RohirrimLancer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rohirrim Lancer", 597, Rarity.COMMON, mage.cards.r.RohirrimLancer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rosie Cotton of South Lane", 27, Rarity.UNCOMMON, mage.cards.r.RosieCottonOfSouthLane.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rosie Cotton of South Lane", 440, Rarity.UNCOMMON, mage.cards.r.RosieCottonOfSouthLane.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Rush the Room", 147, Rarity.COMMON, mage.cards.r.RushTheRoom.class)); - cards.add(new SetCardInfo("Sam's Desperate Rescue", 105, Rarity.COMMON, mage.cards.s.SamsDesperateRescue.class)); + cards.add(new SetCardInfo("Rosie Cotton of South Lane", 478, Rarity.UNCOMMON, mage.cards.r.RosieCottonOfSouthLane.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rush the Room", 147, Rarity.COMMON, mage.cards.r.RushTheRoom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rush the Room", 598, Rarity.COMMON, mage.cards.r.RushTheRoom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sam's Desperate Rescue", 105, Rarity.COMMON, mage.cards.s.SamsDesperateRescue.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sam's Desperate Rescue", 556, Rarity.COMMON, mage.cards.s.SamsDesperateRescue.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Samwise Gamgee", 222, Rarity.RARE, mage.cards.s.SamwiseGamgee.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Samwise Gamgee", 327, Rarity.RARE, mage.cards.s.SamwiseGamgee.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Samwise Gamgee", 673, Rarity.RARE, mage.cards.s.SamwiseGamgee.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Samwise Gamgee", 819, Rarity.RARE, mage.cards.s.SamwiseGamgee.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Samwise the Stouthearted", 28, Rarity.UNCOMMON, mage.cards.s.SamwiseTheStouthearted.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Samwise the Stouthearted", 306, Rarity.UNCOMMON, mage.cards.s.SamwiseTheStouthearted.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Samwise the Stouthearted", 449, Rarity.UNCOMMON, mage.cards.s.SamwiseTheStouthearted.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Samwise the Stouthearted", 479, Rarity.UNCOMMON, mage.cards.s.SamwiseTheStouthearted.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Samwise the Stouthearted", 798, Rarity.UNCOMMON, mage.cards.s.SamwiseTheStouthearted.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Saradoc, Master of Buckland", 282, Rarity.RARE, mage.cards.s.SaradocMasterOfBuckland.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Saradoc, Master of Buckland", 383, Rarity.RARE, mage.cards.s.SaradocMasterOfBuckland.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Saruman of Many Colors", 223, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Saruman of Many Colors", 300, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Saruman of Many Colors", 328, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Saruman of Many Colors", 412, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Saruman the White", 67, Rarity.UNCOMMON, mage.cards.s.SarumanTheWhite.class)); - cards.add(new SetCardInfo("Saruman's Trickery", 68, Rarity.UNCOMMON, mage.cards.s.SarumansTrickery.class)); + cards.add(new SetCardInfo("Saruman of Many Colors", 674, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Saruman of Many Colors", 743, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Saruman of Many Colors", 820, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Saruman the White", 518, Rarity.UNCOMMON, mage.cards.s.SarumanTheWhite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Saruman the White", 67, Rarity.UNCOMMON, mage.cards.s.SarumanTheWhite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Saruman's Trickery", 519, Rarity.UNCOMMON, mage.cards.s.SarumansTrickery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Saruman's Trickery", 68, Rarity.UNCOMMON, mage.cards.s.SarumansTrickery.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron's Ransom", 225, Rarity.RARE, mage.cards.s.SauronsRansom.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron's Ransom", 371, Rarity.RARE, mage.cards.s.SauronsRansom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sauron's Ransom", 676, Rarity.RARE, mage.cards.s.SauronsRansom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sauron's Ransom", 782, Rarity.RARE, mage.cards.s.SauronsRansom.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron, the Dark Lord", 224, Rarity.MYTHIC, mage.cards.s.SauronTheDarkLord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sauron, the Dark Lord", 301, Rarity.MYTHIC, mage.cards.s.SauronTheDarkLord.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron, the Dark Lord", 329, Rarity.MYTHIC, mage.cards.s.SauronTheDarkLord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sauron, the Dark Lord", 675, Rarity.MYTHIC, mage.cards.s.SauronTheDarkLord.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron, the Dark Lord", 744, Rarity.MYTHIC, mage.cards.s.SauronTheDarkLord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sauron, the Dark Lord", 821, Rarity.MYTHIC, mage.cards.s.SauronTheDarkLord.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron, the Lidless Eye", 288, Rarity.MYTHIC, mage.cards.s.SauronTheLidlessEye.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron, the Lidless Eye", 396, Rarity.MYTHIC, mage.cards.s.SauronTheLidlessEye.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron, the Necromancer", 106, Rarity.RARE, mage.cards.s.SauronTheNecromancer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron, the Necromancer", 310, Rarity.RARE, mage.cards.s.SauronTheNecromancer.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Scroll of Isildur", 69, Rarity.RARE, mage.cards.s.ScrollOfIsildur.class)); - cards.add(new SetCardInfo("Second Breakfast", 29, Rarity.COMMON, mage.cards.s.SecondBreakfast.class)); - cards.add(new SetCardInfo("Shadow Summoning", 226, Rarity.UNCOMMON, mage.cards.s.ShadowSummoning.class)); + cards.add(new SetCardInfo("Sauron, the Necromancer", 557, Rarity.RARE, mage.cards.s.SauronTheNecromancer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sauron, the Necromancer", 802, Rarity.RARE, mage.cards.s.SauronTheNecromancer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scroll of Isildur", 520, Rarity.RARE, mage.cards.s.ScrollOfIsildur.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scroll of Isildur", 69, Rarity.RARE, mage.cards.s.ScrollOfIsildur.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Second Breakfast", 29, Rarity.COMMON, mage.cards.s.SecondBreakfast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Second Breakfast", 480, Rarity.COMMON, mage.cards.s.SecondBreakfast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadow Summoning", 226, Rarity.UNCOMMON, mage.cards.s.ShadowSummoning.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadow Summoning", 677, Rarity.UNCOMMON, mage.cards.s.ShadowSummoning.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadow of the Enemy", 107, Rarity.MYTHIC, mage.cards.s.ShadowOfTheEnemy.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadow of the Enemy", 424, Rarity.MYTHIC, mage.cards.s.ShadowOfTheEnemy.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Shadowfax, Lord of Horses", 227, Rarity.UNCOMMON, mage.cards.s.ShadowfaxLordOfHorses.class)); + cards.add(new SetCardInfo("Shadow of the Enemy", 558, Rarity.MYTHIC, mage.cards.s.ShadowOfTheEnemy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadow of the Enemy", 735, Rarity.MYTHIC, mage.cards.s.ShadowOfTheEnemy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadowfax, Lord of Horses", 227, Rarity.UNCOMMON, mage.cards.s.ShadowfaxLordOfHorses.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadowfax, Lord of Horses", 678, Rarity.UNCOMMON, mage.cards.s.ShadowfaxLordOfHorses.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shagrat, Loot Bearer", 228, Rarity.RARE, mage.cards.s.ShagratLootBearer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shagrat, Loot Bearer", 372, Rarity.RARE, mage.cards.s.ShagratLootBearer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shagrat, Loot Bearer", 679, Rarity.RARE, mage.cards.s.ShagratLootBearer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shagrat, Loot Bearer", 783, Rarity.RARE, mage.cards.s.ShagratLootBearer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sharkey, Tyrant of the Shire", 229, Rarity.RARE, mage.cards.s.SharkeyTyrantOfTheShire.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sharkey, Tyrant of the Shire", 373, Rarity.RARE, mage.cards.s.SharkeyTyrantOfTheShire.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Shelob's Ambush", 108, Rarity.COMMON, mage.cards.s.ShelobsAmbush.class)); + cards.add(new SetCardInfo("Sharkey, Tyrant of the Shire", 680, Rarity.RARE, mage.cards.s.SharkeyTyrantOfTheShire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sharkey, Tyrant of the Shire", 784, Rarity.RARE, mage.cards.s.SharkeyTyrantOfTheShire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shelob's Ambush", 108, Rarity.COMMON, mage.cards.s.ShelobsAmbush.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shelob's Ambush", 559, Rarity.COMMON, mage.cards.s.ShelobsAmbush.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shelob, Child of Ungoliant", 230, Rarity.RARE, mage.cards.s.ShelobChildOfUngoliant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shelob, Child of Ungoliant", 374, Rarity.RARE, mage.cards.s.ShelobChildOfUngoliant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shelob, Child of Ungoliant", 681, Rarity.RARE, mage.cards.s.ShelobChildOfUngoliant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shelob, Child of Ungoliant", 785, Rarity.RARE, mage.cards.s.ShelobChildOfUngoliant.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Shire Scarecrow", 249, Rarity.COMMON, mage.cards.s.ShireScarecrow.class)); + cards.add(new SetCardInfo("Shire Scarecrow", 249, Rarity.COMMON, mage.cards.s.ShireScarecrow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shire Scarecrow", 700, Rarity.COMMON, mage.cards.s.ShireScarecrow.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shire Shirriff", 30, Rarity.UNCOMMON, mage.cards.s.ShireShirriff.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shire Shirriff", 441, Rarity.UNCOMMON, mage.cards.s.ShireShirriff.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Shire Terrace", 261, Rarity.COMMON, mage.cards.s.ShireTerrace.class)); - cards.add(new SetCardInfo("Shortcut to Mushrooms", 187, Rarity.UNCOMMON, mage.cards.s.ShortcutToMushrooms.class)); - cards.add(new SetCardInfo("Shower of Arrows", 188, Rarity.COMMON, mage.cards.s.ShowerOfArrows.class)); - cards.add(new SetCardInfo("Slip On the Ring", 31, Rarity.COMMON, mage.cards.s.SlipOnTheRing.class)); + cards.add(new SetCardInfo("Shire Shirriff", 481, Rarity.UNCOMMON, mage.cards.s.ShireShirriff.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shire Terrace", 261, Rarity.COMMON, mage.cards.s.ShireTerrace.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shire Terrace", 712, Rarity.COMMON, mage.cards.s.ShireTerrace.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shortcut to Mushrooms", 187, Rarity.UNCOMMON, mage.cards.s.ShortcutToMushrooms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shortcut to Mushrooms", 638, Rarity.UNCOMMON, mage.cards.s.ShortcutToMushrooms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shower of Arrows", 188, Rarity.COMMON, mage.cards.s.ShowerOfArrows.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shower of Arrows", 639, Rarity.COMMON, mage.cards.s.ShowerOfArrows.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Slip On the Ring", 31, Rarity.COMMON, mage.cards.s.SlipOnTheRing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Slip On the Ring", 482, Rarity.COMMON, mage.cards.s.SlipOnTheRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Smeagol, Helpful Guide", 231, Rarity.RARE, mage.cards.s.SmeagolHelpfulGuide.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Smeagol, Helpful Guide", 330, Rarity.RARE, mage.cards.s.SmeagolHelpfulGuide.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Smite the Deathless", 148, Rarity.COMMON, mage.cards.s.SmiteTheDeathless.class)); - cards.add(new SetCardInfo("Snarling Warg", 109, Rarity.COMMON, mage.cards.s.SnarlingWarg.class)); - cards.add(new SetCardInfo("Soldier of the Grey Host", 32, Rarity.COMMON, mage.cards.s.SoldierOfTheGreyHost.class)); - cards.add(new SetCardInfo("Soothing of Smeagol", 70, Rarity.COMMON, mage.cards.s.SoothingOfSmeagol.class)); + cards.add(new SetCardInfo("Smeagol, Helpful Guide", 682, Rarity.RARE, mage.cards.s.SmeagolHelpfulGuide.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Smeagol, Helpful Guide", 822, Rarity.RARE, mage.cards.s.SmeagolHelpfulGuide.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Smite the Deathless", 148, Rarity.COMMON, mage.cards.s.SmiteTheDeathless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Smite the Deathless", 599, Rarity.COMMON, mage.cards.s.SmiteTheDeathless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Snarling Warg", 109, Rarity.COMMON, mage.cards.s.SnarlingWarg.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Snarling Warg", 560, Rarity.COMMON, mage.cards.s.SnarlingWarg.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldier of the Grey Host", 32, Rarity.COMMON, mage.cards.s.SoldierOfTheGreyHost.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldier of the Grey Host", 483, Rarity.COMMON, mage.cards.s.SoldierOfTheGreyHost.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soothing of Smeagol", 521, Rarity.COMMON, mage.cards.s.SoothingOfSmeagol.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soothing of Smeagol", 70, Rarity.COMMON, mage.cards.s.SoothingOfSmeagol.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spiteful Banditry", 149, Rarity.MYTHIC, mage.cards.s.SpitefulBanditry.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spiteful Banditry", 439, Rarity.MYTHIC, mage.cards.s.SpitefulBanditry.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Stalwarts of Osgiliath", 33, Rarity.COMMON, mage.cards.s.StalwartsOfOsgiliath.class)); - cards.add(new SetCardInfo("Stern Scolding", 71, Rarity.UNCOMMON, mage.cards.s.SternScolding.class)); - cards.add(new SetCardInfo("Stew the Coneys", 189, Rarity.UNCOMMON, mage.cards.s.StewTheConeys.class)); + cards.add(new SetCardInfo("Spiteful Banditry", 600, Rarity.MYTHIC, mage.cards.s.SpitefulBanditry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spiteful Banditry", 738, Rarity.MYTHIC, mage.cards.s.SpitefulBanditry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stalwarts of Osgiliath", 33, Rarity.COMMON, mage.cards.s.StalwartsOfOsgiliath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stalwarts of Osgiliath", 484, Rarity.COMMON, mage.cards.s.StalwartsOfOsgiliath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stern Scolding", 522, Rarity.UNCOMMON, mage.cards.s.SternScolding.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stern Scolding", 71, Rarity.UNCOMMON, mage.cards.s.SternScolding.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stew the Coneys", 189, Rarity.UNCOMMON, mage.cards.s.StewTheConeys.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stew the Coneys", 640, Rarity.UNCOMMON, mage.cards.s.StewTheConeys.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sting, the Glinting Dagger", 250, Rarity.RARE, mage.cards.s.StingTheGlintingDagger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sting, the Glinting Dagger", 409, Rarity.RARE, mage.cards.s.StingTheGlintingDagger.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Stone of Erech", 251, Rarity.UNCOMMON, mage.cards.s.StoneOfErech.class)); + cards.add(new SetCardInfo("Sting, the Glinting Dagger", 701, Rarity.RARE, mage.cards.s.StingTheGlintingDagger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stone of Erech", 251, Rarity.UNCOMMON, mage.cards.s.StoneOfErech.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stone of Erech", 702, Rarity.UNCOMMON, mage.cards.s.StoneOfErech.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Storm of Saruman", 413, Rarity.MYTHIC, mage.cards.s.StormOfSaruman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm of Saruman", 523, Rarity.MYTHIC, mage.cards.s.StormOfSaruman.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Storm of Saruman", 72, Rarity.MYTHIC, mage.cards.s.StormOfSaruman.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Strider, Ranger of the North", 232, Rarity.UNCOMMON, mage.cards.s.StriderRangerOfTheNorth.class)); - cards.add(new SetCardInfo("Surrounded by Orcs", 73, Rarity.COMMON, mage.cards.s.SurroundedByOrcs.class)); + cards.add(new SetCardInfo("Storm of Saruman", 733, Rarity.MYTHIC, mage.cards.s.StormOfSaruman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strider, Ranger of the North", 232, Rarity.UNCOMMON, mage.cards.s.StriderRangerOfTheNorth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strider, Ranger of the North", 683, Rarity.UNCOMMON, mage.cards.s.StriderRangerOfTheNorth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Surrounded by Orcs", 524, Rarity.COMMON, mage.cards.s.SurroundedByOrcs.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Surrounded by Orcs", 73, Rarity.COMMON, mage.cards.s.SurroundedByOrcs.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 266, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 267, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 276, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swamp", 277, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Swarming of Moria", 150, Rarity.COMMON, mage.cards.s.SwarmingOfMoria.class)); - cards.add(new SetCardInfo("Tale of Tinuviel", 34, Rarity.UNCOMMON, mage.cards.t.TaleOfTinuviel.class)); + cards.add(new SetCardInfo("Swamp", 717, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 718, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swarming of Moria", 150, Rarity.COMMON, mage.cards.s.SwarmingOfMoria.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swarming of Moria", 601, Rarity.COMMON, mage.cards.s.SwarmingOfMoria.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tale of Tinuviel", 34, Rarity.UNCOMMON, mage.cards.t.TaleOfTinuviel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tale of Tinuviel", 485, Rarity.UNCOMMON, mage.cards.t.TaleOfTinuviel.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Balrog, Durin's Bane", 195, Rarity.RARE, mage.cards.t.TheBalrogDurinsBane.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Balrog, Durin's Bane", 405, Rarity.RARE, mage.cards.t.TheBalrogDurinsBane.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Balrog, Durin's Bane", 646, Rarity.RARE, mage.cards.t.TheBalrogDurinsBane.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Balrog, Flame of Udun", 297, Rarity.RARE, mage.cards.t.TheBalrogFlameOfUdun.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Balrog, Flame of Udun", 395, Rarity.RARE, mage.cards.t.TheBalrogFlameOfUdun.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("The Bath Song", 40, Rarity.UNCOMMON, mage.cards.t.TheBathSong.class)); + cards.add(new SetCardInfo("The Bath Song", 40, Rarity.UNCOMMON, mage.cards.t.TheBathSong.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Bath Song", 491, Rarity.UNCOMMON, mage.cards.t.TheBathSong.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Battle of Bywater", 2, Rarity.RARE, mage.cards.t.TheBattleOfBywater.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Battle of Bywater", 346, Rarity.RARE, mage.cards.t.TheBattleOfBywater.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("The Black Breath", 78, Rarity.COMMON, mage.cards.t.TheBlackBreath.class)); + cards.add(new SetCardInfo("The Battle of Bywater", 453, Rarity.RARE, mage.cards.t.TheBattleOfBywater.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Battle of Bywater", 757, Rarity.RARE, mage.cards.t.TheBattleOfBywater.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Black Breath", 529, Rarity.COMMON, mage.cards.t.TheBlackBreath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Black Breath", 78, Rarity.COMMON, mage.cards.t.TheBlackBreath.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Grey Havens", 255, Rarity.UNCOMMON, mage.cards.t.TheGreyHavens.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Grey Havens", 443, Rarity.UNCOMMON, mage.cards.t.TheGreyHavens.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("The Mouth of Sauron", 216, Rarity.UNCOMMON, mage.cards.t.TheMouthOfSauron.class)); - cards.add(new SetCardInfo("The One Ring", "001", Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Grey Havens", 706, Rarity.UNCOMMON, mage.cards.t.TheGreyHavens.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Mouth of Sauron", 216, Rarity.UNCOMMON, mage.cards.t.TheMouthOfSauron.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Mouth of Sauron", 667, Rarity.UNCOMMON, mage.cards.t.TheMouthOfSauron.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The One Ring", 0, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The One Ring", 246, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The One Ring", 380, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The One Ring", 451, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The One Ring", 697, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The One Ring", 748, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The One Ring", 791, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Ring Goes South", 186, Rarity.RARE, mage.cards.t.TheRingGoesSouth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Ring Goes South", 366, Rarity.RARE, mage.cards.t.TheRingGoesSouth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Ring Goes South", 637, Rarity.RARE, mage.cards.t.TheRingGoesSouth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Ring Goes South", 777, Rarity.RARE, mage.cards.t.TheRingGoesSouth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Shire", 260, Rarity.RARE, mage.cards.t.TheShire.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Shire", 345, Rarity.RARE, mage.cards.t.TheShire.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("The Torment of Gollum", 110, Rarity.COMMON, mage.cards.t.TheTormentOfGollum.class)); + cards.add(new SetCardInfo("The Shire", 711, Rarity.RARE, mage.cards.t.TheShire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Shire", 756, Rarity.RARE, mage.cards.t.TheShire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Torment of Gollum", 110, Rarity.COMMON, mage.cards.t.TheTormentOfGollum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Torment of Gollum", 561, Rarity.COMMON, mage.cards.t.TheTormentOfGollum.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Watcher in the Water", 354, Rarity.MYTHIC, mage.cards.t.TheWatcherInTheWater.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Watcher in the Water", 526, Rarity.MYTHIC, mage.cards.t.TheWatcherInTheWater.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Watcher in the Water", 734, Rarity.MYTHIC, mage.cards.t.TheWatcherInTheWater.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Watcher in the Water", 75, Rarity.MYTHIC, mage.cards.t.TheWatcherInTheWater.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Watcher in the Water", 765, Rarity.MYTHIC, mage.cards.t.TheWatcherInTheWater.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Theoden, King of Rohan", 233, Rarity.UNCOMMON, mage.cards.t.TheodenKingOfRohan.class)); - cards.add(new SetCardInfo("There and Back Again", 151, Rarity.RARE, mage.cards.t.ThereAndBackAgain.class)); + cards.add(new SetCardInfo("Theoden, King of Rohan", 233, Rarity.UNCOMMON, mage.cards.t.TheodenKingOfRohan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Theoden, King of Rohan", 684, Rarity.UNCOMMON, mage.cards.t.TheodenKingOfRohan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("There and Back Again", 151, Rarity.RARE, mage.cards.t.ThereAndBackAgain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("There and Back Again", 602, Rarity.RARE, mage.cards.t.ThereAndBackAgain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tom Bombadil", 234, Rarity.MYTHIC, mage.cards.t.TomBombadil.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tom Bombadil", 331, Rarity.MYTHIC, mage.cards.t.TomBombadil.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Took Reaper", 35, Rarity.COMMON, mage.cards.t.TookReaper.class)); + cards.add(new SetCardInfo("Tom Bombadil", 685, Rarity.MYTHIC, mage.cards.t.TomBombadil.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tom Bombadil", 745, Rarity.MYTHIC, mage.cards.t.TomBombadil.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tom Bombadil", 823, Rarity.MYTHIC, mage.cards.t.TomBombadil.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Took Reaper", 35, Rarity.COMMON, mage.cards.t.TookReaper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Took Reaper", 486, Rarity.COMMON, mage.cards.t.TookReaper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Trailblazer's Boots", 398, Rarity.RARE, mage.cards.t.TrailblazersBoots.class)); - cards.add(new SetCardInfo("Treason of Isengard", 74, Rarity.COMMON, mage.cards.t.TreasonOfIsengard.class)); - cards.add(new SetCardInfo("Troll of Khazad-dum", 111, Rarity.COMMON, mage.cards.t.TrollOfKhazadDum.class)); - cards.add(new SetCardInfo("Ugluk of the White Hand", 235, Rarity.UNCOMMON, mage.cards.u.UglukOfTheWhiteHand.class)); - cards.add(new SetCardInfo("Uruk-hai Berserker", 112, Rarity.COMMON, mage.cards.u.UrukHaiBerserker.class)); + cards.add(new SetCardInfo("Treason of Isengard", 525, Rarity.COMMON, mage.cards.t.TreasonOfIsengard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Treason of Isengard", 74, Rarity.COMMON, mage.cards.t.TreasonOfIsengard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Troll of Khazad-dum", 111, Rarity.COMMON, mage.cards.t.TrollOfKhazadDum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Troll of Khazad-dum", 562, Rarity.COMMON, mage.cards.t.TrollOfKhazadDum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ugluk of the White Hand", 235, Rarity.UNCOMMON, mage.cards.u.UglukOfTheWhiteHand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ugluk of the White Hand", 686, Rarity.UNCOMMON, mage.cards.u.UglukOfTheWhiteHand.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Uruk-hai Berserker", 112, Rarity.COMMON, mage.cards.u.UrukHaiBerserker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Uruk-hai Berserker", 563, Rarity.COMMON, mage.cards.u.UrukHaiBerserker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Voracious Fell Beast", 113, Rarity.UNCOMMON, mage.cards.v.VoraciousFellBeast.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Voracious Fell Beast", 422, Rarity.UNCOMMON, mage.cards.v.VoraciousFellBeast.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("War of the Last Alliance", 36, Rarity.RARE, mage.cards.w.WarOfTheLastAlliance.class)); - cards.add(new SetCardInfo("Warbeast of Gorgoroth", 152, Rarity.COMMON, mage.cards.w.WarbeastOfGorgoroth.class)); - cards.add(new SetCardInfo("Westfold Rider", 37, Rarity.COMMON, mage.cards.w.WestfoldRider.class)); - cards.add(new SetCardInfo("Willow-Wind", 76, Rarity.COMMON, mage.cards.w.WillowWind.class)); + cards.add(new SetCardInfo("Voracious Fell Beast", 564, Rarity.UNCOMMON, mage.cards.v.VoraciousFellBeast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("War of the Last Alliance", 36, Rarity.RARE, mage.cards.w.WarOfTheLastAlliance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("War of the Last Alliance", 487, Rarity.RARE, mage.cards.w.WarOfTheLastAlliance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Warbeast of Gorgoroth", 152, Rarity.COMMON, mage.cards.w.WarbeastOfGorgoroth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Warbeast of Gorgoroth", 603, Rarity.COMMON, mage.cards.w.WarbeastOfGorgoroth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Warg Rider", 826, Rarity.RARE, mage.cards.w.WargRider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Warg Rider", 831, Rarity.RARE, mage.cards.w.WargRider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Westfold Rider", 37, Rarity.COMMON, mage.cards.w.WestfoldRider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Westfold Rider", 488, Rarity.COMMON, mage.cards.w.WestfoldRider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Willow-Wind", 527, Rarity.COMMON, mage.cards.w.WillowWind.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Willow-Wind", 76, Rarity.COMMON, mage.cards.w.WillowWind.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Witch-king of Angmar", 114, Rarity.MYTHIC, mage.cards.w.WitchKingOfAngmar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Witch-king of Angmar", 311, Rarity.MYTHIC, mage.cards.w.WitchKingOfAngmar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Witch-king of Angmar", 423, Rarity.MYTHIC, mage.cards.w.WitchKingOfAngmar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Witch-king of Angmar", 565, Rarity.MYTHIC, mage.cards.w.WitchKingOfAngmar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Witch-king of Angmar", 736, Rarity.MYTHIC, mage.cards.w.WitchKingOfAngmar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Witch-king of Angmar", 803, Rarity.MYTHIC, mage.cards.w.WitchKingOfAngmar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Witch-king, Bringer of Ruin", 293, Rarity.RARE, mage.cards.w.WitchKingBringerOfRuin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Witch-king, Bringer of Ruin", 391, Rarity.RARE, mage.cards.w.WitchKingBringerOfRuin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Wizard's Rockets", 252, Rarity.COMMON, mage.cards.w.WizardsRockets.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Wizard's Rockets", 400, Rarity.COMMON, mage.cards.w.WizardsRockets.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Wose Pathfinder", 190, Rarity.COMMON, mage.cards.w.WosePathfinder.class)); - cards.add(new SetCardInfo("You Cannot Pass!", 38, Rarity.UNCOMMON, mage.cards.y.YouCannotPass.class)); + cards.add(new SetCardInfo("Wizard's Rockets", 703, Rarity.COMMON, mage.cards.w.WizardsRockets.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wose Pathfinder", 190, Rarity.COMMON, mage.cards.w.WosePathfinder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wose Pathfinder", 641, Rarity.COMMON, mage.cards.w.WosePathfinder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("You Cannot Pass!", 38, Rarity.UNCOMMON, mage.cards.y.YouCannotPass.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("You Cannot Pass!", 489, Rarity.UNCOMMON, mage.cards.y.YouCannotPass.class, NON_FULL_USE_VARIOUS)); } } diff --git a/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java b/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java index 628656e3e6f..c2cbb5c22a7 100644 --- a/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java +++ b/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java @@ -62,10 +62,12 @@ public final class WildsOfEldraineCommander extends ExpansionSet { cards.add(new SetCardInfo("Exotic Orchard", 159, Rarity.RARE, mage.cards.e.ExoticOrchard.class)); cards.add(new SetCardInfo("Fact or Fiction", 90, Rarity.UNCOMMON, mage.cards.f.FactOrFiction.class)); cards.add(new SetCardInfo("Faerie Bladecrafter", 14, Rarity.RARE, mage.cards.f.FaerieBladecrafter.class)); + cards.add(new SetCardInfo("Faerie Conclave", 160, Rarity.UNCOMMON, mage.cards.f.FaerieConclave.class)); cards.add(new SetCardInfo("Faerie Formation", 91, Rarity.RARE, mage.cards.f.FaerieFormation.class)); cards.add(new SetCardInfo("Faerie Seer", 92, Rarity.COMMON, mage.cards.f.FaerieSeer.class)); cards.add(new SetCardInfo("Fellwar Stone", 147, Rarity.UNCOMMON, mage.cards.f.FellwarStone.class)); cards.add(new SetCardInfo("Fertile Ground", 126, Rarity.COMMON, mage.cards.f.FertileGround.class)); + cards.add(new SetCardInfo("Fortified Village", 161, Rarity.RARE, mage.cards.f.FortifiedVillage.class)); cards.add(new SetCardInfo("Frantic Search", 93, Rarity.COMMON, mage.cards.f.FranticSearch.class)); cards.add(new SetCardInfo("Generous Gift", 68, Rarity.UNCOMMON, mage.cards.g.GenerousGift.class)); cards.add(new SetCardInfo("Giant Inheritance", 17, Rarity.RARE, mage.cards.g.GiantInheritance.class)); @@ -121,6 +123,7 @@ public final class WildsOfEldraineCommander extends ExpansionSet { cards.add(new SetCardInfo("Scion of Oona", 109, Rarity.RARE, mage.cards.s.ScionOfOona.class)); cards.add(new SetCardInfo("Secluded Glen", 166, Rarity.RARE, mage.cards.s.SecludedGlen.class)); cards.add(new SetCardInfo("Setessan Champion", 132, Rarity.RARE, mage.cards.s.SetessanChampion.class)); + cards.add(new SetCardInfo("Shadow Puppeteers", 12, Rarity.RARE, mage.cards.s.ShadowPuppeteers.class)); cards.add(new SetCardInfo("Shalai, Voice of Plenty", 74, Rarity.RARE, mage.cards.s.ShalaiVoiceOfPlenty.class)); cards.add(new SetCardInfo("Siona, Captain of the Pyleas", 144, Rarity.UNCOMMON, mage.cards.s.SionaCaptainOfThePyleas.class)); cards.add(new SetCardInfo("Snake Umbra", 133, Rarity.UNCOMMON, mage.cards.s.SnakeUmbra.class)); @@ -143,6 +146,7 @@ public final class WildsOfEldraineCommander extends ExpansionSet { cards.add(new SetCardInfo("Temple of the False God", 172, Rarity.UNCOMMON, mage.cards.t.TempleOfTheFalseGod.class)); cards.add(new SetCardInfo("Theoretical Duplication", 112, Rarity.RARE, mage.cards.t.TheoreticalDuplication.class)); cards.add(new SetCardInfo("Thrilling Encore", 118, Rarity.RARE, mage.cards.t.ThrillingEncore.class)); + cards.add(new SetCardInfo("Throne of Eldraine", 28, Rarity.RARE, mage.cards.t.ThroneOfEldraine.class)); cards.add(new SetCardInfo("Timber Paladin", 20, Rarity.RARE, mage.cards.t.TimberPaladin.class)); cards.add(new SetCardInfo("Timely Ward", 79, Rarity.RARE, mage.cards.t.TimelyWard.class)); cards.add(new SetCardInfo("Tithe Taker", 80, Rarity.RARE, mage.cards.t.TitheTaker.class)); 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 a14ae4ba386..9eb1e3e49bb 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 @@ -1,6 +1,6 @@ package org.mage.test.cards.copy; -import mage.abilities.common.SourceBecomesTargetTriggeredAbility; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; import mage.abilities.keyword.IndestructibleAbility; import mage.abilities.keyword.LifelinkAbility; import mage.constants.PhaseStep; @@ -629,7 +629,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase { assertTrue("Phantasmal Image should not be a creature", !staffA.isCreature(currentGame)); assertTrue("Phantasmal Image should not be an Illusion", !staffA.hasSubtype(SubType.ILLUSION, currentGame)); assertTrue("Phantasmal Image should not be a Construct", !staffA.hasSubtype(SubType.CONSTRUCT, currentGame)); - assertTrue("Phantasmal Image should have the sacrifice trigger", staffA.getAbilities(currentGame).containsClass(SourceBecomesTargetTriggeredAbility.class)); + assertTrue("Phantasmal Image should have the sacrifice trigger", staffA.getAbilities(currentGame).containsClass(BecomesTargetSourceTriggeredAbility.class)); Permanent staffB = getPermanent("Chimeric Staff", playerB); assertTrue("Chimeric Staff should be an artifact", staffB.isArtifact(currentGame)); @@ -659,7 +659,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase { assertTrue("Phantasmal Image should be a Rogue", cloakA.hasSubtype(SubType.ROGUE, currentGame)); assertTrue("Phantasmal Image should be an Illusion", cloakA.hasSubtype(SubType.ILLUSION, currentGame)); assertTrue("Phantasmal Image should be an Equipment", cloakA.hasSubtype(SubType.EQUIPMENT, currentGame)); - assertTrue("Phantasmal Image should have the sacrifice trigger", cloakA.getAbilities(currentGame).containsClass(SourceBecomesTargetTriggeredAbility.class)); + assertTrue("Phantasmal Image should have the sacrifice trigger", cloakA.getAbilities(currentGame).containsClass(BecomesTargetSourceTriggeredAbility.class)); Permanent cloakB = getPermanent("Cloak and Dagger", playerB); assertTrue("Cloak and Dagger should be an artifact", cloakB.isArtifact(currentGame)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/emblems/EmblemOfCardTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/emblems/EmblemOfCardTest.java new file mode 100644 index 00000000000..e480a360f99 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/emblems/EmblemOfCardTest.java @@ -0,0 +1,184 @@ +package org.mage.test.cards.emblems; + +import mage.cards.repository.CardRepository; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.command.emblems.EmblemOfCard; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author artemiswkearney + */ +public class EmblemOfCardTest extends CardTestPlayerBase { + + @Test + public void testEmblemOfGriselbrand() { + // Flying, lifelink + // Pay 7 life: Draw seven cards. + addEmblem(playerA, new EmblemOfCard( + CardRepository.instance.findCard("Griselbrand", true).getMockCard() + )); + + setLife(playerA, 20); + + assertHandCount(playerA, 0); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pay 7 life: Draw"); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertHandCount(playerA, 7); + assertLife(playerA, 13); + assertEmblemCount(playerA, 1); + } + @Test + public void testEmblemOfYurlok() { + // Vigilance + // A player losing unspent mana causes that player to lose that much life. + // {1}, {T}: Each player adds {B}{R}{G}. + addEmblem(playerA, new EmblemOfCard( + CardRepository.instance.findCard("Yurlok of Scorch Thrash", true).getMockCard() + )); + + setLife(playerA, 20); + + // {T}: Add {R}. + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + + checkManaPool("after tapping Mountain", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "R", 1); + checkPlayableAbility("can't tap emblem", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}:", false); + + // wait for mana burn + setStopAt(1, PhaseStep.BEGIN_COMBAT); + + checkLife("takes 1 point of mana burn", 1, PhaseStep.BEGIN_COMBAT, playerA, 19); + execute(); + + assertEmblemCount(playerA, 1); + } + + @Test + public void testEmblemOfOmniscience() { + // You may cast spells from your hand without paying their mana costs. + addEmblem(playerA, new EmblemOfCard( + CardRepository.instance.findCard("Omniscience", true).getMockCard() + )); + + // Colossal Dreadmaw {4}{G}{G} + // Creature - Dinosaur 6/6 + // Trample + addCard(Zone.HAND, playerA, "Colossal Dreadmaw"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Colossal Dreadmaw"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + assertPermanentCount(playerA, "Colossal Dreadmaw", 1); + assertEmblemCount(playerA, 1); + } + @Test + public void testEmblemOfParadoxEngine() { + // Whenever you cast a spell, untap all nonland permanents you control. + addEmblem(playerA, new EmblemOfCard( + CardRepository.instance.findCard("Paradox Engine", true).getMockCard() + )); + + // {T}: Add {G}. + addCard(Zone.BATTLEFIELD, playerA, "Mox Emerald"); + + // Sol Ring {1} + // Artifact + // {T}: Add {C}{C}. + addCard(Zone.HAND, playerA, "Sol Ring"); + + // Basalt Monolith {3} + // Artifact + // Basalt Monolith doesn’t untap during your untap step. + // {T}: Add {C}{C}{C}. + // {3}: Untap Basalt Monolith. + addCard(Zone.HAND, playerA, "Basalt Monolith"); + + // Book of Rass {6} + // Artifact + // {2}, Pay 2 life: Draw a card. + // (just a dummy artifact to cast and spend the mana with) + addCard(Zone.HAND, playerA, "Book of Rass"); + + setLife(playerA, 20); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sol Ring"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Basalt Monolith"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Book of Rass"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, Pay"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, Pay"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, Pay"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 14); + assertEmblemCount(playerA, 1); + } + @Test + public void testEmblemOfDoublingSeason() { + // If an effect would create one or more tokens under your control, it + // creates twice that many of those tokens instead. + // 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. + addEmblem(playerA, new EmblemOfCard( + CardRepository.instance.findCard("Doubling Season", true).getMockCard() + )); + + // {T}: Add {W}. + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + + // Elspeth, Sun's Champion {4}{W}{W} + // Legendary Planeswalker — Elspeth + // +1: Create three 1/1 white Soldier creature tokens. + // −3: Destroy all creatures with power 4 or greater. + // −7: You get an emblem with “Creatures you control get +2/+2 and have flying.” + // Loyalty: 4 + addCard(Zone.HAND, playerA, "Elspeth, Sun's Champion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elspeth, Sun's Champion"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCounters( + "Elspeth's loyalty is doubled", + 1, + PhaseStep.PRECOMBAT_MAIN, + playerA, + "Elspeth, Sun's Champion", + CounterType.LOYALTY, + 8 + ); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Create"); + checkPlayableAbility("can't still activate Griselbrand", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pay 7 life:", false); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCounters( + "+1 is not doubled", + 1, + PhaseStep.PRECOMBAT_MAIN, + playerA, + "Elspeth, Sun's Champion", + CounterType.LOYALTY, + 9 + ); + checkPermanentCount( + "Soldier tokens doubled", + 1, + PhaseStep.PRECOMBAT_MAIN, + playerA, + "Soldier Token", + 6 + ); + execute(); + assertEmblemCount(playerA, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/EnchantedPermanentLKITest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/EnchantedPermanentLKITest.java new file mode 100644 index 00000000000..7edc7fc054a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/EnchantedPermanentLKITest.java @@ -0,0 +1,135 @@ +package org.mage.test.cards.enchantments; + +import mage.abilities.keyword.FlyingAbility; +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 xenohedron + */ +public class EnchantedPermanentLKITest extends CardTestPlayerBase { + + // See issue #6326 and issue #11210 + + private static final String skyrider = "Skyrider Trainee"; // {4}{W} 3/3 + // Skyrider Trainee has flying as long as it's enchanted. + private static final String golem = "Thran Golem"; // {5} 3/3 + // As long as Thran Golem is enchanted, it gets +2/+2 and has flying, first strike, and trample. + private static final String rest = "Winter's Rest"; // {1}{U} Aura + // When Winter's Rest enters the battlefield, tap enchanted creature. + // As long as you control another snow permanent, enchanted creature doesn't untap during its controller's untap step. + private static final String bind = "Bind the Monster"; // {U} Aura + // When Bind the Monster enters the battlefield, tap enchanted creature. It deals damage to you equal to its power. + // Enchanted creature doesn't untap during its controller's untap step. + private static final String disperse = "Disperse"; // {1}{U} Instant + // Return target nonland permanent to its owner's hand. + private static final String apathy = "Dreadful Apathy"; // {2}{W} Aura + // Enchanted creature can't attack or block. + private static final String apathyAbility = "{2}{W}: Exile enchanted creature."; + private static final String flicker = "Flicker of Fate"; // {1}{W} Instant + // Exile target creature or enchantment, then return it to the battlefield under its owner’s control. + private static final String finesse = "Aura Finesse"; // {U} Instant + // Attach target Aura you control to target creature. Draw a card. + private static final String tundra = "Tundra"; // UW Dual land + + @Test + public void testAuraRemoved() { + addCard(Zone.BATTLEFIELD, playerA, tundra, 4); + addCard(Zone.BATTLEFIELD, playerA, skyrider); + addCard(Zone.HAND, playerA, rest); + addCard(Zone.HAND, playerA, disperse); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rest, skyrider); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); + checkAbility("Skyrider flying", 1, PhaseStep.PRECOMBAT_MAIN, playerA, skyrider, FlyingAbility.class, true); + // Creature enchanted, but trigger on stack + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, disperse, rest); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, disperse, 1); + assertTapped(skyrider, true); + assertHandCount(playerA, rest, 1); + } + + @Test + public void testAuraRemovedDamaging() { + addCard(Zone.BATTLEFIELD, playerA, tundra, 3); + addCard(Zone.BATTLEFIELD, playerA, skyrider); + addCard(Zone.HAND, playerA, bind); + addCard(Zone.HAND, playerA, disperse); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bind, skyrider); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); + checkAbility("Skyrider flying", 1, PhaseStep.PRECOMBAT_MAIN, playerA, skyrider, FlyingAbility.class, true); + // Creature enchanted, but trigger on stack + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, disperse, bind); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, disperse, 1); + assertTapped(skyrider, true); + assertHandCount(playerA, bind, 1); + assertLife(playerA, 17); + } + + @Test + public void testAuraFlickered() { + addCard(Zone.BATTLEFIELD, playerA, tundra, 8); + addCard(Zone.BATTLEFIELD, playerA, skyrider); + addCard(Zone.BATTLEFIELD, playerA, golem); + addCard(Zone.HAND, playerA, apathy); + addCard(Zone.HAND, playerA, flicker); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, apathy, skyrider); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkAbility("Skyrider flying", 1, PhaseStep.PRECOMBAT_MAIN, playerA, skyrider, FlyingAbility.class, true); + activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, apathyAbility); + // In response: + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, flicker, apathy); + setChoice(playerA, golem); // when aura returns, choose what to enchant + // Skyrider should be exiled by the ability resolving (LKI of Dreadful Apathy enchanted creature) + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, flicker, 1); + assertExileCount(skyrider, 1); + assertAbility(playerA, golem, TrampleAbility.getInstance(), true); // now enchanted + } + + @Test + public void testAuraMoved() { + addCard(Zone.BATTLEFIELD, playerA, tundra, 7); + addCard(Zone.BATTLEFIELD, playerA, skyrider); + addCard(Zone.BATTLEFIELD, playerA, golem); + addCard(Zone.HAND, playerA, apathy); + addCard(Zone.HAND, playerA, finesse); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, apathy, skyrider); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkAbility("Skyrider flying", 1, PhaseStep.PRECOMBAT_MAIN, playerA, skyrider, FlyingAbility.class, true); + activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, apathyAbility); + // In response: + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, finesse, apathy); + addTarget(playerA, golem); // second target + // Golem should be exiled by the ability resolving (same zcc Dreadful Apathy) + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, finesse, 1); + assertExileCount(golem, 1); + assertAbility(playerA, skyrider, FlyingAbility.getInstance(), false); // no longer enchanted + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/rules/NamePredicateTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/rules/NamePredicateTest.java index 477fd073b3e..ac8037ba789 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/rules/NamePredicateTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/rules/NamePredicateTest.java @@ -50,4 +50,30 @@ public class NamePredicateTest extends CardTestPlayerBase { assertNamePredicate("by inner - non existing name must return zero", 0, "Island", true); assertNamePredicate("by inner - existing name must work", 3, "Forest", true); } + + @Test + public void testCityInABottle() { + String bottle = "City in a Bottle"; // Artifact {2} + // Whenever one or more other nontoken permanents with a name originally printed in the Arabian Nights expansion + // are on the battlefield, their controllers sacrifice them. + // Players can’t cast spells or play lands with a name originally printed in the Arabian Nights expansion. + String nomads = "Desert Nomads"; // Creature {2}{R} (originally printed in Arabian Nights) + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); // printed in Arabian Nights, but first printed in Alpha + addCard(Zone.BATTLEFIELD, playerA, "Camel"); // originally printed in Arabian Nights + addCard(Zone.HAND, playerA, bottle); + addCard(Zone.HAND, playerA, nomads); + + checkPlayableAbility("Nomads, unbottled", 1, PhaseStep.PRECOMBAT_MAIN , playerA, "Cast Desert Nomads", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bottle); + checkGraveyardCount("Camel sacrificed" , 1, PhaseStep.BEGIN_COMBAT, playerA, "Camel", 1); + checkPlayableAbility("Nomads, bottled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Desert Nomads", false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, 1); + assertPermanentCount(playerA, bottle, 1); + assertPermanentCount(playerA, "Mountain", 5); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/c17/ScalelordReckonerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c17/ScalelordReckonerTest.java new file mode 100644 index 00000000000..36182bd1c81 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c17/ScalelordReckonerTest.java @@ -0,0 +1,39 @@ +package org.mage.test.cards.single.c17; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author xenohedron + */ +public class ScalelordReckonerTest extends CardTestPlayerBase { + + private static final String scalelord = "Scalelord Reckoner"; // 4/4 Flying + // Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, + // destroy target nonland permanent that player controls. + + @Test + public void testScalelordReckoner() { + + String machete = "Trusty Machete"; // a nonland permanent to destroy + String shock = "Shock"; // a spell to target with + + addCard(Zone.BATTLEFIELD, playerA, scalelord); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerB, machete, 1); + addCard(Zone.HAND, playerB, shock, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, shock, scalelord); + addTarget(playerA, machete); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertDamageReceived(playerA, scalelord, 2); + assertGraveyardCount(playerB, machete, 1); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/DanithaBenaliasHopeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/DanithaBenaliasHopeTest.java new file mode 100644 index 00000000000..f6b7e534670 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/DanithaBenaliasHopeTest.java @@ -0,0 +1,119 @@ +package org.mage.test.cards.single.dmu; + +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.FlyingAbility; +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 xenohedron + */ +public class DanithaBenaliasHopeTest extends CardTestPlayerBase { + + private static final String danitha = "Danitha, Benalia's Hope"; // {4}{W} 4/4 First strike, vigilance, lifelink + // When Danitha, Benalia's Hope enters the battlefield, you may put an Aura or Equipment card + // from your hand or graveyard onto the battlefield attached to Danitha. + private static final String wings = "Nimbus Wings"; // Enchanted creature gets +1/+2 and has flying. + private static final String flail = "Gorgon Flail"; // Equipped creature gets +1/+1 and has deathtouch. + private static final String swords = "Swords to Plowshares"; // Exile target creature. Its controller gains life equal to its power. + + @Test + public void testAuraFromGraveyard() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.GRAVEYARD, playerA, wings); + addCard(Zone.HAND, playerA, danitha); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, danitha); + setChoice(playerA, true); // use ability + setChoice(playerA, wings); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPowerToughness(playerA, danitha, 5, 6); + assertAbility(playerA, danitha, FlyingAbility.getInstance(), true); + } + + @Test + public void testNothingToAttach() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.HAND, playerA, danitha); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, danitha); + setChoice(playerA, true); // attempt to use ability + setChoice(playerA, TestPlayer.CHOICE_SKIP); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPowerToughness(playerA, danitha, 4, 4); + } + + @Test + public void testEquipmentFromHand() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.HAND, playerA, flail); + addCard(Zone.HAND, playerA, danitha); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, danitha); + setChoice(playerA, true); // use ability + setChoice(playerA, flail); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPowerToughness(playerA, danitha, 5, 5); + assertAbility(playerA, danitha, DeathtouchAbility.getInstance(), true); + } + + @Test + public void testEquipmentFromHandButExiled() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + addCard(Zone.HAND, playerA, flail); + addCard(Zone.HAND, playerA, danitha); + addCard(Zone.HAND, playerA, swords); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, danitha); + setChoice(playerA, true); // use ability + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, swords, danitha); + setChoice(playerA, flail); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, flail, 1); + assertExileCount(playerA, danitha, 1); + assertLife(playerA, 24); + } + + @Test + public void testAuraFromGraveyardButExiled() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + addCard(Zone.GRAVEYARD, playerA, wings); + addCard(Zone.HAND, playerA, danitha); + addCard(Zone.HAND, playerA, swords); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, danitha); + setChoice(playerA, true); // use ability + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, swords, danitha); + setChoice(playerA, TestPlayer.CHOICE_SKIP); // no longer can attach Aura + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, wings, 1); + assertExileCount(playerA, danitha, 1); + assertLife(playerA, 24); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/j22/AgrusKosEternalSoldierTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/j22/AgrusKosEternalSoldierTest.java new file mode 100644 index 00000000000..0df627d9194 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/j22/AgrusKosEternalSoldierTest.java @@ -0,0 +1,70 @@ +package org.mage.test.cards.single.j22; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author xenohedron + */ +public class AgrusKosEternalSoldierTest extends CardTestPlayerBase { + + private static final String agrus = "Agrus Kos, Eternal Soldier"; // 3/4 Vigilance + // Whenever Agrus Kos, Eternal Soldier becomes the target of an ability that targets only it, you may pay {1}{R/W}. + // If you do, copy that ability for each other creature you control that ability could target. Each copy targets a different one of those creatures. + private static final String sparkmage = "Cunning Sparkmage"; // 0/1 Haste pinger + private static final String turtle = "Horned Turtle"; // 1/4 + private static final String firewalker = "Kor Firewalker"; // 2/2 protection from red + // Whenever a player casts a red spell, you may gain 1 life. + + @Test + public void testTriggerCopiesAbility() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, agrus); + addCard(Zone.BATTLEFIELD, playerA, turtle); + addCard(Zone.BATTLEFIELD, playerA, firewalker); + addCard(Zone.BATTLEFIELD, playerB, sparkmage); + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: {this} deals", agrus); + setChoice(playerA, true); // pay to copy + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertTapped(sparkmage, true); + assertTappedCount("Plateau", true, 2); + assertDamageReceived(playerA, agrus, 1); + assertDamageReceived(playerA, turtle, 1); + assertDamageReceived(playerA, firewalker, 0); + assertLife(playerA, 20); + assertLife(playerB, 20); + } + + @Test + public void testTriggerDoesntCopySpell() { + setStrictChooseMode(true); + + String shock = "Shock"; + addCard(Zone.BATTLEFIELD, playerA, agrus); + addCard(Zone.BATTLEFIELD, playerA, turtle); + addCard(Zone.BATTLEFIELD, playerA, firewalker); + addCard(Zone.HAND, playerB, shock); + addCard(Zone.BATTLEFIELD, playerB, "Plateau"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, shock, agrus); + setChoice(playerA, true); // gain 1 life + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertDamageReceived(playerA, agrus, 2); + assertDamageReceived(playerA, turtle, 0); + assertDamageReceived(playerA, firewalker, 0); + assertLife(playerA, 21); + assertLife(playerB, 20); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/vow/BrineComberTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/vow/BrineComberTest.java index a15f46b2c07..5dd058345d9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/vow/BrineComberTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/vow/BrineComberTest.java @@ -14,10 +14,11 @@ public class BrineComberTest extends CardTestPlayerBase { * Brine Comber * {1}{W}{U} * Creature — Spirit - * * Whenever Brine Comber enters the battlefield or becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying. - * * Disturb {W}{U} (You may cast this card from your graveyard transformed for its disturb cost.) + * Brinebound Gift + * Enchant creature + * Whenever Brinebound Gift enters the battlefield or enchanted creature becomes the target of an Aura spell, create a 1/1 white Spirit creature token with flying. */ private static final String comber = "Brine Comber"; @@ -74,4 +75,26 @@ public class BrineComberTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Hopeful Eidolon", 1); assertPowerToughness(playerA, comber, 1+1, 1+1); } + + @Test + public void testDisturbSideTrigger() { + setStrictChooseMode(true); + + String hatchling = "Kraken Hatchling"; // 0/4 + String umbra = "Hyena Umbra"; // Aura - gives +1/+1 and first strike + addCard(Zone.BATTLEFIELD, playerA, hatchling); + addCard(Zone.BATTLEFIELD, playerA, "Tundra", 3); + addCard(Zone.GRAVEYARD, playerA, comber); + addCard(Zone.HAND, playerA, umbra); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Brinebound Gift using Disturb", hatchling); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, umbra, hatchling); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Spirit Token", 2); // one from enter, one from target of next Aura + assertPermanentCount(playerA, "Brinebound Gift", 1); + assertPowerToughness(playerA, hatchling, 1, 5); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTargetTriggerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTargetTriggerTest.java new file mode 100644 index 00000000000..6141bbd80e7 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTargetTriggerTest.java @@ -0,0 +1,457 @@ +package org.mage.test.cards.triggers; + +import mage.abilities.keyword.FlyingAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.Filter; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class BecomesTargetTriggerTest extends CardTestPlayerBase { + + @Test + public void testAshenmoorLiege() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.HAND, playerA, "Claustrophobia"); // {1}{U}{U} + + // Other black creatures you control get +1/+1. + // Other red creatures you control get +1/+1. + // Whenever Ashenmoor Liege becomes the target of a spell or ability an opponent controls, that player loses 4 life. + addCard(Zone.BATTLEFIELD, playerB, "Ashenmoor Liege", 1); // 4/1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Claustrophobia", "Ashenmoor Liege"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 16); + assertPermanentCount(playerA, "Claustrophobia", 1); + assertPowerToughness(playerB, "Ashenmoor Liege", 4, 1); + } + + @Test + public void testVeneratedRotpriest() { + String rotpriest = "Venerated Rotpriest"; // 1/2 + // Whenever a creature you control becomes the target of a spell, target opponent gets a poison counter. + String growth = "Giant Growth"; + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, rotpriest); + addCard(Zone.HAND, playerA, growth); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, growth, rotpriest); + addTarget(playerA, playerB); // to get a poison counter + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertCounterCount(playerB, CounterType.POISON, 1); + assertPowerToughness(playerA, rotpriest, 4, 5); + } + + private static final String gKeeper = "Glyph Keeper"; // 5/3 Flying + // Whenever this creature becomes the target of a spell or ability for the first time in a turn, counter that spell or ability. + private static final String bolt = "Lightning Bolt"; + + @Test + public void testGlyphKeeperCountersFirstSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, bolt); + + addCard(Zone.BATTLEFIELD, playerB, gKeeper); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, bolt, 1); + assertPermanentCount(playerB, gKeeper, 1); + } + + @Test + public void testGlyphKeeperCountersFirstSpellEachTurn() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, bolt, 2); + + addCard(Zone.BATTLEFIELD, playerB, gKeeper); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, bolt, 2); + assertPermanentCount(playerB, gKeeper, 1); + } + + @Test + public void testGlyphKeeperCountersFirstSpellButNotSecondSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, bolt, 2); + + addCard(Zone.BATTLEFIELD, playerB, gKeeper); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, bolt, gKeeper); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.DECLARE_ATTACKERS); + execute(); + + assertGraveyardCount(playerA, bolt, 2); + assertPermanentCount(playerB, gKeeper, 0); + } + + @Test + public void testGlyphKeeperCountersFirstAbilityButNotSecondOne() { + /* + Soulstinger {3}{B} + Creature - Scorpion Demon 4/5 + When Soulstinger enters the battlefield, put two -1/-1 counter on target creature you control. + When Soulstinger dies, you may put a -1/-1 counter on target creature for each -1/-1 counter on Soulstinger. + */ + String sStinger = "Soulstinger"; + + /* + Cartouche of Strength {2}{G} + Enchantment - Aura Cartouche + Enchant creature you control + When Cartouche of Strength enters the battlefield, you may have enchanted creature fight target creature an opponent controls. + Enchanted creature gets +1/+1 and has trample. + */ + String cStrength = "Cartouche of Strength"; + String memnite = "Memnite"; // {0} 1/1 + + addCard(Zone.BATTLEFIELD, playerA, gKeeper); + addCard(Zone.HAND, playerA, sStinger); + addCard(Zone.HAND, playerA, cStrength); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + addCard(Zone.BATTLEFIELD, playerB, memnite); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sStinger); + addTarget(playerA, gKeeper); // should be countered by Glyph Keeper clause as first ability targetting it + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cStrength, gKeeper); // should not be countered anymore + setChoice(playerA, true); + addTarget(playerA, memnite); // Cartouche of Strength fight + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + setStrictChooseMode(true); + execute(); + + assertPermanentCount(playerA, gKeeper, 1); + assertGraveyardCount(playerA, sStinger, 0); // countered + assertGraveyardCount(playerA, cStrength, 0); // should not be countered + assertPermanentCount(playerA, cStrength, 1); + assertGraveyardCount(playerB, memnite, 1); // dies from fight + assertPowerToughness(playerA, gKeeper, 6, 4, Filter.ComparisonScope.All); + // Soul Stinger should never have given it two -1/-1 counters + // And Cartouche of Strength gives +1/+1 + assertCounterCount(playerA, gKeeper, CounterType.M1M1, 0); + } + + @Test + public void testDiffusionSliver() { + String diffusion = "Diffusion Sliver"; // 1/1 Sliver + // Whenever a Sliver creature you control becomes the target of a spell or ability an opponent controls, + // counter that spell or ability unless its controller pays {2}. + String metallic = "Metallic Sliver"; // 1/1 Sliver + + addCard(Zone.BATTLEFIELD, playerA, diffusion); + addCard(Zone.BATTLEFIELD, playerA, metallic); + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, bolt, 1); + addCard(Zone.BATTLEFIELD, playerB, "Cunning Sparkmage", 1); // 0/1 Haste + // {T}: Cunning Sparkmage deals 1 damage to any target. + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, bolt, metallic); + setChoice(playerB, false); // choose not to pay + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "{T}: {this} deals", diffusion); + setChoice(playerB, false); // choose not to pay + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, bolt, 1); + assertTapped("Cunning Sparkmage", true); + assertPermanentCount(playerA, diffusion, 1); + assertPermanentCount(playerA, metallic, 1); + } + + @Test + public void testThunderbreakRegent() { + String dragon = "Thunderbreak Regent"; // 4/4 + // Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, Thunderbreak Regent deals 3 damage to that player. + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, dragon); + addCard(Zone.HAND, playerB, bolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, bolt, dragon); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertDamageReceived(playerA, dragon, 3); + assertLife(playerB, 17); + } + + @Test + public void testCloudCover() { + String cloud = "Cloud Cover"; // Enchantment + // Whenever another permanent you control becomes the target of a spell or ability an opponent controls, + // you may return that permanent to its owner’s hand. + String myr = "Omega Myr"; // 1/2 + + addCard(Zone.BATTLEFIELD, playerB, "Cunning Sparkmage"); + addCard(Zone.BATTLEFIELD, playerA, cloud); + addCard(Zone.BATTLEFIELD, playerA, myr); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: {this} deals", myr); + setChoice(playerA, true); // return to hand + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertHandCount(playerA, myr, 1); + assertTapped("Cunning Sparkmage", true); + } + + @Test + public void testIllusionaryArmor() { + String armor = "Illusionary Armor"; // Enchantment - Aura {4}{U} + // Enchant creature + // Enchanted creature gets +4/+4. + // When enchanted creature becomes the target of a spell or ability, sacrifice Illusionary Armor. + String beast = "Axebane Beast"; // 3/4 + + addCard(Zone.BATTLEFIELD, playerA, beast); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + addCard(Zone.HAND, playerA, armor); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, bolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, armor, beast); + checkPT("Boosted", 1, PhaseStep.BEGIN_COMBAT, playerA, beast, 7, 8); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, bolt, beast); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, armor, 1); + assertPowerToughness(playerA, beast, 3, 4); + assertDamageReceived(playerA, beast, 3); + } + + @Test + public void testFracturedLoyalty() { + String hatchling = "Kraken Hatchling"; // 0/4 + String aura = "Fractured Loyalty"; // Enchantment - Aura {1}{R} + // Whenever enchanted creature becomes the target of a spell or ability, + // that spell or ability's controller gains control of that creature. (This effect lasts indefinitely.) + + addCard(Zone.BATTLEFIELD, playerA, hatchling); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, aura); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, bolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, aura, hatchling); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, bolt, hatchling); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, bolt, 1); + assertPermanentCount(playerA, hatchling, 0); + assertDamageReceived(playerB, hatchling, 3); + } + + @Test + public void testDormantGomazoa() { + String gomazoa = "Dormant Gomazoa"; // 5/5 Flying {1}{U}{U} + // Dormant Gomazoa enters the battlefield tapped. + // Dormant Gomazoa doesn't untap during your untap step. + // Whenever you become the target of a spell, you may untap Dormant Gomazoa. + + addCard(Zone.HAND, playerA, gomazoa); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, bolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, gomazoa); + checkPermanentTapped("Enters tapped", 1, PhaseStep.BEGIN_COMBAT, playerA, gomazoa, true, 1); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, bolt, playerA); + setChoice(playerA, true); // choose to untap + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, bolt, 1); + assertTapped(gomazoa, false); + assertLife(playerA, 17); + } + + private static final String mammoth = "Battle Mammoth"; // 6/5 Trample + // Whenever a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. + private static final String bear = "Runeclaw Bear"; // 2/2 + private static final String cBond = "Common Bond"; // 1GW Instant + // Put a +1/+1 counter on target creature. Put a +1/+1 counter on target creature. + + @Test + public void testBattleMammothSeparateTargets() { + addCard(Zone.BATTLEFIELD, playerA, mammoth); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.BATTLEFIELD, playerB, "Savannah", 3); + addCard(Zone.HAND, playerB, cBond); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, cBond, mammoth+"^"+bear); + setChoice(playerA, "Whenever"); // order two triggers + setChoice(playerA, true); // draw a card + setChoice(playerA, true); // draw a card + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, cBond, 1); + assertPowerToughness(playerA, mammoth, 7, 6); + assertPowerToughness(playerA, bear, 3, 3); + assertHandCount(playerA, 2); + } + + @Test + public void testBattleMammothSameTarget() { + addCard(Zone.BATTLEFIELD, playerA, mammoth); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.BATTLEFIELD, playerB, "Savannah", 3); + addCard(Zone.HAND, playerB, cBond); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, cBond, bear+"^"+bear); + // Should be only one trigger here + setChoice(playerA, true); // draw a card + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, cBond, 1); + assertPowerToughness(playerA, bear, 4, 4); + assertHandCount(playerA, 1); + } + + @Test + public void testBattleMammothRepeatAbility() { + addCard(Zone.BATTLEFIELD, playerA, mammoth); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 8); + addCard(Zone.BATTLEFIELD, playerB, "Shapers of Nature"); // 3/3 + // {3}{G}: Put a +1/+1 counter on target creature. + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{3}{G}: Put", bear); + setChoice(playerA, true); // draw a card + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{3}{G}: Put", bear); + setChoice(playerA, true); // draw a card + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, bear, 4, 4); + assertHandCount(playerA, 2); + } + + @Test + public void testAngelicCubDoubleTarget() { + String cub = "Angelic Cub"; // 1/1 + // Whenever Angelic Cub becomes the target of a spell or ability for the first time each turn, put a +1/+1 counter on it. + // As long as Angelic Cub has three or more +1/+1 counters on it, it has flying. + + addCard(Zone.BATTLEFIELD, playerA, cub); + addCard(Zone.BATTLEFIELD, playerA, "Savannah", 3); + addCard(Zone.HAND, playerA, cBond); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cBond, cub+"^"+cub); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, cub, 4, 4); + assertAbility(playerA, cub, FlyingAbility.getInstance(), true); + } + + @Test + public void testUnsettledMarinerFieldOfRuin() { + // Reported bug: #10530 + String mariner = "Unsettled Mariner"; // 2/2 + // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, + // counter that spell or ability unless its controller pays {1}. + String ruin = "Field of Ruin"; // 2/2 + // {2}, {T}, Sacrifice Field of Ruin: Destroy target nonbasic land an opponent controls. + // Each player searches their library for a basic land card, puts it onto the battlefield, then shuffles. + + addCard(Zone.BATTLEFIELD, playerA, mariner); + addCard(Zone.BATTLEFIELD, playerA, "Evolving Wilds"); + addCard(Zone.BATTLEFIELD, playerB, ruin); + addCard(Zone.BATTLEFIELD, playerB, "Wastes", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{2}, {T}, Sacrifice"); + addTarget(playerB, "Evolving Wilds"); + setChoice(playerB, false); // choose not to pay + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, 0); + assertGraveyardCount(playerB, ruin, 1); + } + + @Test + public void testCounterAbilitySacrificedSource() { + // related to #10530, but this one works fine regardless... + String mariner = "Unsettled Mariner"; // 2/2 + // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, + // counter that spell or ability unless its controller pays {1}. + String felidar = "Felidar Cub"; // 2/2 + // Sacrifice Felidar Cub: Destroy target enchantment. + String anthem = "Glorious Anthem"; // Creatures you control get +1/+1. + + addCard(Zone.BATTLEFIELD, playerA, mariner); + addCard(Zone.BATTLEFIELD, playerA, anthem); + addCard(Zone.BATTLEFIELD, playerB, felidar); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Sacrifice"); + addTarget(playerB, anthem); + setChoice(playerB, false); // choose not to pay + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, felidar, 1); + assertPowerToughness(playerA, mariner, 3, 3); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/TargetedTriggeredTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/TargetedTriggeredTest.java deleted file mode 100644 index a58df8f083f..00000000000 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/TargetedTriggeredTest.java +++ /dev/null @@ -1,183 +0,0 @@ - -package org.mage.test.cards.triggers; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import mage.counters.CounterType; -import mage.filter.Filter; -import org.junit.Ignore; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class TargetedTriggeredTest extends CardTestPlayerBase { - - /** - * Tests that the first spell that targets Kira, Great Glass-Spinner is - * countered. - * - */ - @Test - // this does not currently work in test (????), because the target event will be fired earlier during tests, - // so the zone change counter for the fixed target of the counterspell will not work - // UPDATE: seems to work fine now? 04/19/2017 escplan9 - public void testKiraGreatGlassSpinnerFirstSpellTurn() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); - addCard(Zone.HAND, playerA, "Lightning Bolt"); - - addCard(Zone.BATTLEFIELD, playerB, "Kira, Great Glass-Spinner", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Kira, Great Glass-Spinner"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, "Lightning Bolt", 1); - - assertPermanentCount(playerB, "Kira, Great Glass-Spinner", 1); - } - - /** - * With Ashenmoor Liege on the battlefield, my opponent casts Claustrophobia - * on it without losing 4hp. - */ - @Test - public void testAshenmoorLiege() { - addCard(Zone.BATTLEFIELD, playerA, "Island", 3); - addCard(Zone.HAND, playerA, "Claustrophobia"); // {1}{U}{U} - - // Other black creatures you control get +1/+1. - // Other red creatures you control get +1/+1. - // Whenever Ashenmoor Liege becomes the target of a spell or ability an opponent controls, that player loses 4 life. - addCard(Zone.BATTLEFIELD, playerB, "Ashenmoor Liege", 1); // 4/1 - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Claustrophobia", "Ashenmoor Liege"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertLife(playerA, 16); - - assertPermanentCount(playerA, "Claustrophobia", 1); - assertPowerToughness(playerB, "Ashenmoor Liege", 4, 1); - } - - @Test - public void testGlyphKeeperCountersFirstSpell() { - - /* - Glyph Keeper {3}{U}{U} - Creature - Sphinx - Flying 5/3 - Whenever this creature becomes the target of a spell or ability for the first time in a turn, counter that spell or ability." - */ - String gKeeper = "Glyph Keeper"; - String bolt = "Lightning Bolt"; // {R} instant deal 3 dmg - - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); - addCard(Zone.HAND, playerA, bolt); - - addCard(Zone.BATTLEFIELD, playerB, gKeeper); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertGraveyardCount(playerA, bolt, 1); - assertPermanentCount(playerB, gKeeper, 1); - } - - @Test - public void testGlyphKeeperCountersFirstSpellButNotSecondSpell() { - - /* - Glyph Keeper {3}{U}{U} - Creature - Sphinx - Flying 5/3 - Whenever this creature becomes the target of a spell or ability for the first time in a turn, counter that spell or ability." - */ - String gKeeper = "Glyph Keeper"; - String bolt = "Lightning Bolt"; // {R} instant deal 3 dmg - - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); - addCard(Zone.HAND, playerA, bolt, 2); - - addCard(Zone.BATTLEFIELD, playerB, gKeeper); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, gKeeper); - castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, bolt, gKeeper); - - setStopAt(1, PhaseStep.DECLARE_ATTACKERS); - execute(); - - assertGraveyardCount(playerA, bolt, 2); - assertPermanentCount(playerB, gKeeper, 0); - } - - /* - NOTE: test was failing due to card bug, resolved as of 04/20/2017. See issue #3180 - I had a Glyph Keeper on board (cloned with Vizier of many faces). -- note this test is a simplified version, next test will test on the Clone if needed - First I played a Soulstinger and targeted the Glyph Keeper, the ability was countered. Then on the same main phase I played a Cartouche of Strength targeting the Glyph Keeper, that was also countered. - Only the first should have been countered. - */ - @Test - public void testGlyphKeeperCountersFirstAbilityButNotSecondOne() { - - /* - Glyph Keeper {3}{U}{U} - Creature - Sphinx - Flying 5/3 - Whenever this creature becomes the target of a spell or ability for the first time in a turn, counter that spell or ability." - */ - String gKeeper = "Glyph Keeper"; - - /* - Soulstinger {3}{B} - Creature - Scorpion Demon 4/5 - When Soulstinger enters the battlefield, put two -1/-1 counter on target creature you control. - When Soulstinger dies, you may put a -1/-1 counter on target creature for each -1/-1 counter on Soulstinger. - */ - String sStinger = "Soulstinger"; - - /* - Cartouche of Strength {2}{G} - Enchantment - Aura Cartouche - Enchant creature you control - When Cartouche of Strength enters the battlefield, you may have enchanted creature fight target creature an opponent controls. - Enchanted creature gets +1/+1 and has trample. - */ - String cStrength = "Cartouche of Strength"; - String memnite = "Memnite"; // {0} 1/1 - - addCard(Zone.BATTLEFIELD, playerA, gKeeper); - addCard(Zone.HAND, playerA, sStinger); - addCard(Zone.HAND, playerA, cStrength); - addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); - addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); - addCard(Zone.BATTLEFIELD, playerB, memnite); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sStinger); - addTarget(playerA, gKeeper); // should be countered by Glyph Keeper clause as first ability targetting it - waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cStrength, gKeeper); // should not be countered anymore - setChoice(playerA, true); - addTarget(playerA, memnite); // Cartouche of Strength fight - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - setStrictChooseMode(true); - execute(); - - assertPermanentCount(playerA, gKeeper, 1); - assertGraveyardCount(playerA, sStinger, 0); // countered - assertGraveyardCount(playerA, cStrength, 0); // should not be countered - assertPermanentCount(playerA, cStrength, 1); - assertGraveyardCount(playerB, memnite, 1); // dies from fight - assertPowerToughness(playerA, gKeeper, 6, 4, Filter.ComparisonScope.All); // Soul Stinger should never have given it two -1/-1 counters - //And Cartouche of Strength gives +1/+1 - assertCounterCount(playerA, gKeeper, CounterType.M1M1, 0); - } -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/KiraGreatGlassSpinnerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/KiraGreatGlassSpinnerTest.java index bb493a311b0..b07ff85a931 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/KiraGreatGlassSpinnerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/KiraGreatGlassSpinnerTest.java @@ -17,17 +17,19 @@ public class KiraGreatGlassSpinnerTest extends CardTestPlayerBase { Flying Creatures you control have "Whenever this creature becomes the target of a spell or ability for the first time each turn, counter that spell or ability." */ - private final String kira = "Kira, Great Glass-Spinner"; - private final String ugin = "Ugin, the Spirit Dragon"; + private static final String kira = "Kira, Great Glass-Spinner"; + private static final String ugin = "Ugin, the Spirit Dragon"; + private static final String bolt = "Lightning Bolt"; @Test public void counterFirst() { - addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyality counters + addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyalty counters addCard(Zone.BATTLEFIELD, playerA, kira); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: {this} deals 3 damage", kira); // Ugin ability + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); @@ -38,8 +40,7 @@ public class KiraGreatGlassSpinnerTest extends CardTestPlayerBase { @Test public void counterFirstResolveSecond() { - String ugin = "Ugin, the Spirit Dragon"; - addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyality counters + addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyalty counters addCard(Zone.BATTLEFIELD, playerA, "Island", 4); addCard(Zone.HAND, playerA, "Unsummon", 1); @@ -48,6 +49,7 @@ public class KiraGreatGlassSpinnerTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unsummon", kira); + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); @@ -59,12 +61,13 @@ public class KiraGreatGlassSpinnerTest extends CardTestPlayerBase { @Test public void counterFirstThisTurn_counterFirstOnNextTurn() { - addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyality counters + addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyalty counters addCard(Zone.BATTLEFIELD, playerA, kira); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: {this} deals 3 damage to", kira); // Ugin ability activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: {this} deals 3 damage to", kira); // Ugin ability + setStrictChooseMode(true); setStopAt(3, PhaseStep.END_TURN); execute(); @@ -72,4 +75,105 @@ public class KiraGreatGlassSpinnerTest extends CardTestPlayerBase { assertCounterCount(playerA, ugin, CounterType.LOYALTY, 11); } + + @Test + public void testKiraGreatGlassSpinnerFirstSpellTurn() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerB, kira, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, kira); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, bolt, 1); + assertPermanentCount(playerB, kira, 1); + } + + @Test + public void testKiraGreatGlassSpinnerRightAbility() { + // Reported bug: #8026 + addCard(Zone.BATTLEFIELD, playerA, "Steadfast Guard", 1); // 2/2 Vigilance + addCard(Zone.BATTLEFIELD, playerA, "Angelic Benediction", 1); // Exalted + // Whenever a creature you control attacks alone, you may tap target creature. + addCard(Zone.BATTLEFIELD, playerB, kira, 1); + + attack(1, playerA, "Steadfast Guard", playerB); + setChoice(playerA, "Whenever"); // choose trigger order - exalted at top of stack + addTarget(playerA, kira); // creature to tap + //setChoice(playerA, true); // choose yes to tap - gets countered before choice needed + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 17); // exalted pumps 2/2 to 3/3, so 3 life lost + assertTapped(kira, false); // tap ability was countered + } + + @Test + public void testKiraGreatGlassSpinnerRightAbilityOtherOrder() { + // Reported bug: #8026 + addCard(Zone.BATTLEFIELD, playerA, "Steadfast Guard", 1); // 2/2 Vigilance + addCard(Zone.BATTLEFIELD, playerA, "Angelic Benediction", 1); // Exalted + // Whenever a creature you control attacks alone, you may tap target creature. + addCard(Zone.BATTLEFIELD, playerB, kira, 1); + + attack(1, playerA, "Steadfast Guard", playerB); + setChoice(playerA, "exalted"); // just in case... try the other trigger order + addTarget(playerA, kira); // creature to tap + //setChoice(playerA, true); // choose yes to tap - gets countered before choice needed + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 17); // exalted pumps 2/2 to 3/3, so 3 life lost + assertTapped(kira, false); // tap ability was countered + } + + @Test + public void counterFirstBlinkCounterNext() { + addCard(Zone.BATTLEFIELD, playerA, ugin); // starts with 7 Loyalty counters + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 2); + addCard(Zone.HAND, playerA, "Shock", 1); + addCard(Zone.HAND, playerA, "Cloudshift", 1); + addCard(Zone.BATTLEFIELD, playerA, kira); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2: {this} deals 3 damage", kira); // Ugin ability, countered + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Cloudshift", kira); // not countered (second time being targeted) + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Shock", kira); // countered (zcc has changed, so first time again) + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, kira, 1); + assertCounterCount(playerA, ugin, CounterType.LOYALTY, 9); + assertGraveyardCount(playerA, "Shock", 1); + assertGraveyardCount(playerA, "Cloudshift", 1); + } + + @Test + public void counterCorrectlyMultipleOnStack() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + addCard(Zone.HAND, playerA, "Battlegrowth", 1); // Instant {G} Put a +1/+1 counter on target creature. + addCard(Zone.HAND, playerA, "Dragonscale Boon", 1); // Instant {3}{G} Put two +1/+1 counters on target creature and untap it. + addCard(Zone.BATTLEFIELD, playerA, kira); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Battlegrowth", kira); // countered + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dragonscale Boon", kira); // not countered (second target) + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, kira, 1); + assertGraveyardCount(playerA, "Battlegrowth", 1); + assertGraveyardCount(playerA, "Dragonscale Boon", 1); + assertPowerToughness(playerA, kira, 4, 4); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/combat/CombatDamageByToughnessTest.java b/Mage.Tests/src/test/java/org/mage/test/combat/CombatDamageByToughnessTest.java new file mode 100644 index 00000000000..2bad7a572e6 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/combat/CombatDamageByToughnessTest.java @@ -0,0 +1,84 @@ +package org.mage.test.combat; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class CombatDamageByToughnessTest extends CardTestPlayerBase { + + @Test + public void testByToughnessAll() { + addCard(Zone.BATTLEFIELD, playerA, "Doran, the Siege Tower", 1); // 0/5 + // Each creature assigns combat damage equal to its toughness rather than its power. + addCard(Zone.BATTLEFIELD, playerB, "Kraken Hatchling", 1); // 0/4 + + attack(1, playerA, "Doran, the Siege Tower"); + attack(2, playerB, "Kraken Hatchling"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 4); + assertLife(playerB, 20 - 5); + } + + @Test + public void testByToughnessControlled() { + addCard(Zone.BATTLEFIELD, playerA, "Belligerent Brontodon", 1); // 4/6 + // Each creature you control assigns combat damage equal to its toughness rather than its power. + addCard(Zone.BATTLEFIELD, playerB, "Maritime Guard", 1); // 1/3 + + attack(1, playerA, "Belligerent Brontodon"); + attack(2, playerB, "Maritime Guard"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 1); + assertLife(playerB, 20 - 6); + } + + @Test + public void testByToughnessFilter() { + addCard(Zone.BATTLEFIELD, playerA, "Ancient Lumberknot", 1); // 1/4 + // Each creature you control with toughness greater than its power assigns combat damage equal to its toughness rather than its power. + addCard(Zone.BATTLEFIELD, playerA, "Goblin Piker", 1); // 2/1 + + attack(1, playerA, "Ancient Lumberknot"); + attack(1, playerA, "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 4 - 2); + } + + @Test + public void testByToughnessTarget() { + addCard(Zone.BATTLEFIELD, playerA, "Walking Bulwark", 1); // 0/3 Defender + // {2}: Until end of turn, target creature with defender gains haste, can attack as though it didn't have defender, + // and assigns combat damage equal to its toughness rather than its power. Activate only as a sorcery. + addCard(Zone.BATTLEFIELD, playerA, "Wild-Field Scarecrow", 1); // 1/4 Defender + addCard(Zone.BATTLEFIELD, playerA, "Wastes", 2); + addCard(Zone.BATTLEFIELD, playerA, "Serra's Blessing", 1); // Creatures you control have vigilance + addCard(Zone.BATTLEFIELD, playerB, "Barony Vampire", 1); // 3/2 + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}: Until", "Wild-Field Scarecrow"); + attack(1, playerA, "Wild-Field Scarecrow"); + attack(2, playerB, "Barony Vampire"); + block(2, playerA, "Wild-Field Scarecrow", "Barony Vampire"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 4); + assertDamageReceived(playerB, "Barony Vampire", 1); + assertDamageReceived(playerA, "Wild-Field Scarecrow", 3); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/load/LoadCallbackClient.java b/Mage.Tests/src/test/java/org/mage/test/load/LoadCallbackClient.java index 3efdc2347ff..e5a6d99afaa 100644 --- a/Mage.Tests/src/test/java/org/mage/test/load/LoadCallbackClient.java +++ b/Mage.Tests/src/test/java/org/mage/test/load/LoadCallbackClient.java @@ -45,7 +45,7 @@ public class LoadCallbackClient implements CallbackClient { // ignore bloaded logs switch (callback.getMethod()) { case CHATMESSAGE: - case GAME_INFORM: + case GAME_UPDATE_AND_INFORM: case GAME_UPDATE: break; default: @@ -77,7 +77,7 @@ public class LoadCallbackClient implements CallbackClient { break; } - case GAME_INFORM: + case GAME_UPDATE_AND_INFORM: case GAME_INFORM_PERSONAL: { GameClientMessage message = (GameClientMessage) callback.getData(); gameView = message.getGameView(); diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index b6f25a4f098..7a104419a9b 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -63,7 +63,7 @@ public class VerifyCardDataTest { private static final Logger logger = Logger.getLogger(VerifyCardDataTest.class); - private static final String FULL_ABILITIES_CHECK_SET_CODES = "WOE;WOC"; // check ability text due mtgjson, can use multiple sets like MAT;CMD or * for all + private static final String FULL_ABILITIES_CHECK_SET_CODES = "LTR;LTC;WOC;LCI;LCC;REX"; // check ability text due mtgjson, can use multiple sets like MAT;CMD or * for all private static final boolean CHECK_ONLY_ABILITIES_TEXT = false; // use when checking text locally, suppresses unnecessary checks and output messages private static final boolean AUTO_FIX_SAMPLE_DECKS = false; // debug only: auto-fix sample decks by test_checkSampleDecks test run diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 01485b42ca2..3ec9f12bac1 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -1480,6 +1480,13 @@ public abstract class AbilityImpl implements Ability { } public AbilityImpl copyWithZone(Zone zone) { + if (this instanceof MageSingleton) { + // not safe to change zone for singletons + // in theory there could be some sort of wrapper to effectively change + // the zone here, but currently no use of copyWithZone actually needs + // to change the zone of any existing singleton abilities + return this; + } AbilityImpl copy = ((AbilityImpl)this.copy()); copy.zone = zone; copy.newId(); diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbility.java b/Mage/src/main/java/mage/abilities/TriggeredAbility.java index 430ce33e549..66a6d85bd89 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbility.java @@ -17,10 +17,6 @@ public interface TriggeredAbility extends Ability { * This check for the relevant event types is called at first to prevent * further actions if the current event is ignored from this triggered * ability - * - * @param event - * @param game - * @return */ boolean checkEventType(GameEvent event, Game game); @@ -30,10 +26,6 @@ public interface TriggeredAbility extends Ability { * multiple times. Because some abilities call this to check if an ability * is relevant (e.g. Torpor Orb), so the method is called multiple times for * the same event. - * - * @param event - * @param game - * @return */ boolean checkTrigger(GameEvent event, Game game); @@ -45,7 +37,10 @@ public interface TriggeredAbility extends Ability { TriggeredAbility setDoOnlyOnceEachTurn(boolean doOnlyOnce); - TriggeredAbility setReplaceRuleText(boolean replaceRuleText); + /** + * if true, replaces "{this}" with "it" in the effect text + */ + TriggeredAbility withRuleTextReplacement(boolean replaceRuleText); boolean checkInterveningIfClause(Game game); diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java index 916114e6700..a5e62043668 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java @@ -27,7 +27,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge protected boolean leavesTheBattlefieldTrigger; private boolean triggersOnceEachTurn = false; private boolean doOnlyOnceEachTurn = false; - protected boolean replaceRuleText = true; + protected boolean replaceRuleText = false; // if true, replace "{this}" with "it" in effect text private GameEvent triggerEvent = null; private String triggerPhrase = null; @@ -131,7 +131,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge } @Override - public TriggeredAbility setReplaceRuleText(boolean replaceRuleText) { + public TriggeredAbility withRuleTextReplacement(boolean replaceRuleText) { this.replaceRuleText = replaceRuleText; return this; } @@ -220,18 +220,8 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge superRule = superRule.replaceFirst(" (become|block|deal|discard|gain|get|lose|mill|sacrifice)s? ", " $1 "); } } - if (replaceRuleText - && triggerPhrase != null - && triggerPhrase.contains("{this}") - && !triggerPhrase.contains("other") - && !triggerPhrase.contains(" of a ") - && !triggerPhrase.contains(" by a ") - && !triggerPhrase.contains(" to a ") - && !triggerPhrase.contains(" blocks a ") - && (superRule.startsWith("{this}") - || superRule.startsWith("sacrifice {this}") - )) { - superRule = superRule.replace("{this} ", "it "); + if (replaceRuleText && triggerPhrase != null) { + superRule = superRule.replaceFirst("^(sacrifice )?\\{this\\}", "$1it"); } sb.append(superRule); if (triggersOnceEachTurn) { diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAloneSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAloneSourceTriggeredAbility.java index 66dbdb21663..76295c94fff 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksAloneSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksAloneSourceTriggeredAbility.java @@ -16,6 +16,7 @@ public class AttacksAloneSourceTriggeredAbility extends TriggeredAbilityImpl { public AttacksAloneSourceTriggeredAbility(Effect effect) { super(Zone.BATTLEFIELD, effect); setTriggerPhrase("Whenever {this} attacks alone, "); + this.replaceRuleText = true; // default true to replace "{this}" with "it" } protected AttacksAloneSourceTriggeredAbility(final AttacksAloneSourceTriggeredAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java index 783d7c97b72..8db14e1f72c 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java @@ -24,6 +24,7 @@ public class AttacksAndIsNotBlockedTriggeredAbility extends TriggeredAbilityImpl super(Zone.BATTLEFIELD, effect, optional); this.setTargetPointer = setTargetPointer; setTriggerPhrase("Whenever {this} attacks and isn't blocked, "); + this.replaceRuleText = true; // default true to replace "{this}" with "it" } protected AttacksAndIsNotBlockedTriggeredAbility(final AttacksAndIsNotBlockedTriggeredAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/common/AttacksOrBlocksTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksOrBlocksTriggeredAbility.java index b4c98191436..9f837aa7eb8 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksOrBlocksTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksOrBlocksTriggeredAbility.java @@ -16,6 +16,7 @@ public class AttacksOrBlocksTriggeredAbility extends TriggeredAbilityImpl { } else { setTriggerPhrase("Whenever {this} attacks or blocks, "); } + this.replaceRuleText = true; // default true to replace "{this}" with "it" } protected AttacksOrBlocksTriggeredAbility(final AttacksOrBlocksTriggeredAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/common/AttacksTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksTriggeredAbility.java index 6d63fac493d..f12ed09e6fc 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksTriggeredAbility.java @@ -35,6 +35,7 @@ public class AttacksTriggeredAbility extends TriggeredAbilityImpl { this.text = text; this.setTargetPointer = setTargetPointer; setTriggerPhrase("Whenever {this} attacks, "); + this.replaceRuleText = true; // default true to replace "{this}" with "it" } protected AttacksTriggeredAbility(final AttacksTriggeredAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java index 5877b26c4f6..78ce6cff2d5 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesBlockedSourceTriggeredAbility.java @@ -22,6 +22,7 @@ public class BecomesBlockedSourceTriggeredAbility extends TriggeredAbilityImpl { super(Zone.BATTLEFIELD, effect, optional); this.setTargetPointer = setTargetPointer; setTriggerPhrase("Whenever {this} becomes blocked, "); + this.replaceRuleText = true; // default true to replace "{this}" with "it" } protected BecomesBlockedSourceTriggeredAbility(final BecomesBlockedSourceTriggeredAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTappedSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTappedSourceTriggeredAbility.java index f5deca395e5..9824e66ef03 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTappedSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTappedSourceTriggeredAbility.java @@ -19,6 +19,7 @@ public class BecomesTappedSourceTriggeredAbility extends TriggeredAbilityImpl { public BecomesTappedSourceTriggeredAbility(Effect effect, boolean isOptional) { super(Zone.BATTLEFIELD, effect, isOptional); setTriggerPhrase("Whenever {this} becomes tapped, "); + this.replaceRuleText = true; // default true to replace "{this}" with "it" } protected BecomesTappedSourceTriggeredAbility(final BecomesTappedSourceTriggeredAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java new file mode 100644 index 00000000000..e65f68dd59a --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetAnyTriggeredAbility.java @@ -0,0 +1,97 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.FilterStackObject; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * @author xenohedron + */ +public class BecomesTargetAnyTriggeredAbility extends TriggeredAbilityImpl { + + private final FilterPermanent filterTarget; + private final FilterStackObject filterStack; + private final SetTargetPointer setTargetPointer; + + public BecomesTargetAnyTriggeredAbility(Effect effect, FilterPermanent filterTarget) { + this(effect, filterTarget, StaticFilters.FILTER_SPELL_OR_ABILITY_A); + } + + /** + * "Whenever [a filterTarget] becomes the target of [a filterStack], [effect]" + * @param effect defaults to SetTargetPointer.PERMANENT + * @param filterTarget permanents to check being targetted + * @param filterStack spells/abilities to check targeting + */ + public BecomesTargetAnyTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack) { + this(effect, filterTarget, filterStack, SetTargetPointer.PERMANENT, false); + } + + public BecomesTargetAnyTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack, + SetTargetPointer setTargetPointer, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + this.filterTarget = filterTarget; + this.filterStack = filterStack; + this.setTargetPointer = setTargetPointer; + setTriggerPhrase("Whenever " + filterTarget.getMessage() + " becomes the target of " + + filterStack.getMessage() + ", "); + } + + protected BecomesTargetAnyTriggeredAbility(final BecomesTargetAnyTriggeredAbility ability) { + super(ability); + this.filterTarget = ability.filterTarget; + this.filterStack = ability.filterStack; + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public BecomesTargetAnyTriggeredAbility copy() { + return new BecomesTargetAnyTriggeredAbility(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 || !filterTarget.match(permanent, getControllerId(), this, game)) { + return false; + } + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || !filterStack.match(targetingObject, getControllerId(), this, game)) { + return false; + } + if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) { + return false; + } + switch (setTargetPointer) { + case PERMANENT: + this.getAllEffects().setTargetPointer(new FixedTarget(permanent.getId(), game)); + break; + case PLAYER: + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getControllerId(), game)); + break; + case SPELL: + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); + break; + case NONE: + break; + default: + throw new IllegalArgumentException("Unsupported SetTargetPointer in BecomesTargetAnyTriggeredAbility"); + } + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java index 125dbc3c882..b4098303ae0 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetAttachedTriggeredAbility.java @@ -2,6 +2,7 @@ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.FilterStackObject; import mage.filter.StaticFilters; @@ -9,6 +10,8 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.StackObject; import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @author LoneFox @@ -16,24 +19,23 @@ import mage.game.permanent.Permanent; public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl { private final FilterStackObject filter; + private final SetTargetPointer setTargetPointer; public BecomesTargetAttachedTriggeredAbility(Effect effect) { - this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A); + this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A, SetTargetPointer.NONE, false); } - public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter) { - this(effect, filter, "creature"); - } - - public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter, String enchantType) { - super(Zone.BATTLEFIELD, effect); - this.filter = filter.copy(); - setTriggerPhrase("When enchanted " + enchantType + " becomes the target of " + filter.getMessage() + ", "); + public BecomesTargetAttachedTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + this.filter = filter; + this.setTargetPointer = setTargetPointer; + setTriggerPhrase("When enchanted creature becomes the target of " + filter.getMessage() + ", "); } protected BecomesTargetAttachedTriggeredAbility(final BecomesTargetAttachedTriggeredAbility ability) { super(ability); - this.filter = ability.filter.copy(); + this.filter = ability.filter; + this.setTargetPointer = ability.setTargetPointer; } @Override @@ -49,13 +51,28 @@ public class BecomesTargetAttachedTriggeredAbility extends TriggeredAbilityImpl @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent enchantment = game.getPermanent(sourceId); - StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); - if (enchantment != null && enchantment.getAttachedTo() != null) { - if (event.getTargetId().equals(enchantment.getAttachedTo()) - && filter.match(sourceObject, getControllerId(), this, game)) { - return true; - } + if (enchantment == null || enchantment.getAttachedTo() == null || !event.getTargetId().equals(enchantment.getAttachedTo())) { + return false; } - return false; + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || !filter.match(targetingObject, getControllerId(), this, game)) { + return false; + } + if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) { + return false; + } + switch (setTargetPointer) { + case PLAYER: + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getControllerId(), game)); + break; + case SPELL: + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); + break; + case NONE: + break; + default: + throw new IllegalArgumentException("Unsupported SetTargetPointer in BecomesTargetAttachedTriggeredAbility"); + } + return true; } } diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerSpellTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerSpellTriggeredAbility.java deleted file mode 100644 index c7322c34017..00000000000 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerSpellTriggeredAbility.java +++ /dev/null @@ -1,45 +0,0 @@ - - -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.game.events.GameEvent; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.stack.Spell; - -/** - * @author jeffwadsworth - */ -public class BecomesTargetControllerSpellTriggeredAbility extends TriggeredAbilityImpl { - - public BecomesTargetControllerSpellTriggeredAbility(Effect effect, boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); - setTriggerPhrase("When you become the target of a spell, "); - } - - protected BecomesTargetControllerSpellTriggeredAbility(final BecomesTargetControllerSpellTriggeredAbility ability) { - super(ability); - } - - @Override - public BecomesTargetControllerSpellTriggeredAbility copy() { - return new BecomesTargetControllerSpellTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(controllerId)) { - if (game.getObject(event.getSourceId()) instanceof Spell) { - return true; - } - } - return false; - } -} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java new file mode 100644 index 00000000000..ccb9a6dde2f --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetControllerTriggeredAbility.java @@ -0,0 +1,84 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.filter.FilterPermanent; +import mage.filter.FilterStackObject; +import mage.game.events.GameEvent; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * @author xenohedron + */ +public class BecomesTargetControllerTriggeredAbility extends TriggeredAbilityImpl { + + private final FilterPermanent filterTarget; + private final FilterStackObject filterStack; + private final SetTargetPointer setTargetPointer; + + /** + * Note: filterTarget can be null for "whenever you become the target of..."; + * if set, then "whenever you or a [filterTarget] becomes the target of..." + */ + public BecomesTargetControllerTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack, + SetTargetPointer setTargetPointer, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + this.filterTarget = filterTarget; + this.filterStack = filterStack; + this.setTargetPointer = setTargetPointer; + String filterMessage = (filterTarget == null) + ? "you become" + : "you or " + filterTarget.getMessage() + " becomes"; + setTriggerPhrase("Whenever " + filterMessage + " the target of " + filterStack.getMessage() + ", "); + } + + protected BecomesTargetControllerTriggeredAbility(final BecomesTargetControllerTriggeredAbility ability) { + super(ability); + this.filterTarget = ability.filterTarget; + this.filterStack = ability.filterStack; + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public BecomesTargetControllerTriggeredAbility copy() { + return new BecomesTargetControllerTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TARGETED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getTargetId().equals(getControllerId())) { + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent == null || filterTarget == null || !filterTarget.match(permanent, getControllerId(), this, game)) { + return false; + } + } + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || !filterStack.match(targetingObject, getControllerId(), this, game)) { + return false; + } + if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) { + return false; + } + switch (setTargetPointer) { + case SPELL: + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); + break; + case NONE: + break; + default: + throw new IllegalArgumentException("Unsupported SetTargetPointer in BecomesTargetControllerTriggeredAbility"); + } + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetOpponentAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetOpponentAllTriggeredAbility.java deleted file mode 100644 index 598ee6ad0a3..00000000000 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetOpponentAllTriggeredAbility.java +++ /dev/null @@ -1,83 +0,0 @@ -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; -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 BecomesTargetOpponentAllTriggeredAbility extends TriggeredAbilityImpl { - - private final FilterPermanent filter; - - public BecomesTargetOpponentAllTriggeredAbility(Effect effect, boolean optional) { - this(effect, StaticFilters.FILTER_CONTROLLED_A_PERMANENT, optional); - } - - public BecomesTargetOpponentAllTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); - this.filter = filter; - setTriggerPhrase("Whenever " + filter + " becomes the target of a spell or ability an opponent controls, "); - } - - private BecomesTargetOpponentAllTriggeredAbility(final BecomesTargetOpponentAllTriggeredAbility ability) { - super(ability); - this.filter = ability.filter; - } - - @Override - public BecomesTargetOpponentAllTriggeredAbility copy() { - return new BecomesTargetOpponentAllTriggeredAbility(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 || !filter.match(permanent, getControllerId(), this, game)) { - 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; - } -} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceFirstTimeTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceFirstTimeTriggeredAbility.java new file mode 100644 index 00000000000..bf895161c74 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceFirstTimeTriggeredAbility.java @@ -0,0 +1,41 @@ +package mage.abilities.common; + +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.filter.FilterStackObject; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.common.NumberOfTimesPermanentTargetedATurnWatcher; + +/** + * @author xenohedron + */ +public class BecomesTargetSourceFirstTimeTriggeredAbility extends BecomesTargetSourceTriggeredAbility { + + public BecomesTargetSourceFirstTimeTriggeredAbility(Effect effect, FilterStackObject filter, + SetTargetPointer setTargetPointer, boolean optional) { + super(effect, filter, setTargetPointer, optional); + this.addWatcher(new NumberOfTimesPermanentTargetedATurnWatcher()); + setTriggerPhrase("Whenever {this} becomes the target of " + filter.getMessage() + " for the first time each turn, "); + } + + protected BecomesTargetSourceFirstTimeTriggeredAbility(final BecomesTargetSourceFirstTimeTriggeredAbility ability) { + super(ability); + } + + @Override + public BecomesTargetSourceFirstTimeTriggeredAbility copy() { + return new BecomesTargetSourceFirstTimeTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + NumberOfTimesPermanentTargetedATurnWatcher watcher = game.getState().getWatcher(NumberOfTimesPermanentTargetedATurnWatcher.class); + if (permanent == null || watcher == null || watcher.numTimesTargetedThisTurn(permanent, game) > 1) { + return false; + } + return super.checkTrigger(event, game); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java new file mode 100644 index 00000000000..09ff5c1c1d4 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetSourceTriggeredAbility.java @@ -0,0 +1,89 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.effects.common.ReturnToHandSourceEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.FilterStackObject; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.StackObject; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +/** + * @author North + */ +public class BecomesTargetSourceTriggeredAbility extends TriggeredAbilityImpl { + + private final FilterStackObject filter; + private final SetTargetPointer setTargetPointer; + + public BecomesTargetSourceTriggeredAbility(Effect effect) { + this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A); + } + + public BecomesTargetSourceTriggeredAbility(Effect effect, FilterStackObject filter) { + this(effect, filter, SetTargetPointer.NONE, false); + } + + public BecomesTargetSourceTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + this.filter = filter; + this.setTargetPointer = setTargetPointer; + boolean textWhen = (effect instanceof SacrificeSourceEffect + || effect instanceof ReturnToHandSourceEffect + || effect instanceof ShuffleIntoLibrarySourceEffect + || effect instanceof ExileSourceEffect); + setTriggerPhrase((textWhen ? "When" : "Whenever") + " {this} becomes the target of " + filter.getMessage() + ", "); + this.replaceRuleText = true; // default true to replace "{this}" with "it" + } + + protected BecomesTargetSourceTriggeredAbility(final BecomesTargetSourceTriggeredAbility ability) { + super(ability); + this.filter = ability.filter; + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public BecomesTargetSourceTriggeredAbility copy() { + return new BecomesTargetSourceTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TARGETED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!event.getTargetId().equals(getSourceId())) { + return false; + } + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || !filter.match(targetingObject, getControllerId(), this, game)) { + return false; + } + if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) { + return false; + } + switch (setTargetPointer) { + case PLAYER: + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getControllerId(), game)); + break; + case SPELL: + this.getAllEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); + break; + case NONE: + break; + default: + throw new IllegalArgumentException("Unsupported SetTargetPointer in BecomesTargetSourceTriggeredAbility"); + } + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java deleted file mode 100644 index cbd330ebb13..00000000000 --- a/Mage/src/main/java/mage/abilities/common/BecomesTargetTriggeredAbility.java +++ /dev/null @@ -1,73 +0,0 @@ -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.FilterStackObject; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; -import mage.target.targetpointer.FixedTarget; - -/** - * @author Susucr - */ -public class BecomesTargetTriggeredAbility extends TriggeredAbilityImpl { - - private final FilterPermanent filterTarget; - private final FilterStackObject filterStack; - - public BecomesTargetTriggeredAbility(Effect effect, FilterPermanent filterTarget) { - this(effect, filterTarget, StaticFilters.FILTER_SPELL_OR_ABILITY_A); - } - - public BecomesTargetTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack) { - this(effect, filterTarget, filterStack, false); - } - - public BecomesTargetTriggeredAbility(Effect effect, FilterPermanent filterTarget, FilterStackObject filterStack, - boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); - this.filterTarget = filterTarget; - this.filterStack = filterStack; - setTriggerPhrase("Whenever " + filterTarget.getMessage() + " becomes the target of " - + filterStack.getMessage() + ", "); - } - - protected BecomesTargetTriggeredAbility(final BecomesTargetTriggeredAbility ability) { - super(ability); - this.filterTarget = ability.filterTarget; - this.filterStack = ability.filterStack; - } - - @Override - public BecomesTargetTriggeredAbility copy() { - return new BecomesTargetTriggeredAbility(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 - || !filterStack.match(sourceObject, getControllerId(), this, game)) { - return false; - } - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent == null - || !filterTarget.match(permanent, getControllerId(), this, game)) { - return false; - } - - getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); - - return true; - } -} diff --git a/Mage/src/main/java/mage/abilities/common/CycleTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/CycleTriggeredAbility.java index 051b4914573..cad25db019a 100644 --- a/Mage/src/main/java/mage/abilities/common/CycleTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CycleTriggeredAbility.java @@ -14,13 +14,14 @@ public class CycleTriggeredAbility extends ZoneChangeTriggeredAbility { public CycleTriggeredAbility(Effect effect, boolean optional) { super(Zone.ALL, effect, "When you cycle {this}, ", optional); + this.replaceRuleText = true; // default true to replace "{this}" with "it" } public CycleTriggeredAbility(Effect effect) { this(effect, false); } - public CycleTriggeredAbility(CycleTriggeredAbility ability) { + protected CycleTriggeredAbility(CycleTriggeredAbility ability) { super(ability); } diff --git a/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java index ea0bcc58473..98bbd454eb2 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java @@ -15,6 +15,7 @@ public class DiesSourceTriggeredAbility extends ZoneChangeTriggeredAbility { public DiesSourceTriggeredAbility(Effect effect, boolean optional) { super(Zone.BATTLEFIELD, Zone.GRAVEYARD, effect, "When {this} dies, ", optional); + this.replaceRuleText = true; // default true to replace "{this}" with "it" } public DiesSourceTriggeredAbility(Effect effect) { diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldTriggeredAbility.java index 9b220920f78..4177879a831 100644 --- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldTriggeredAbility.java @@ -11,27 +11,18 @@ import mage.game.events.GameEvent; */ public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl { - protected boolean ignoreRulesGeneration; // use it with custom rules (if you don't want ETB auto-generated text) - protected String etbFlavorWord = null; - public EntersBattlefieldTriggeredAbility(Effect effect) { this(effect, false); } public EntersBattlefieldTriggeredAbility(Effect effect, boolean optional) { - this(effect, optional, false); - } - - public EntersBattlefieldTriggeredAbility(Effect effect, boolean optional, boolean ignoreRulesGeneration) { super(Zone.ALL, effect, optional); // Zone.All because a creature with trigger can be put into play and be sacrificed during the resolution of an effect (discard Obstinate Baloth with Smallpox) - this.ignoreRulesGeneration = ignoreRulesGeneration; + this.replaceRuleText = true; // default true to replace "{this}" with "it" setTriggerPhrase("When {this} enters the battlefield, "); } protected EntersBattlefieldTriggeredAbility(final EntersBattlefieldTriggeredAbility ability) { super(ability); - this.ignoreRulesGeneration = ability.ignoreRulesGeneration; - this.etbFlavorWord = ability.etbFlavorWord; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldUntappedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldUntappedTriggeredAbility.java index 22cd31cf5f0..c3f9f5d05b8 100644 --- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldUntappedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldUntappedTriggeredAbility.java @@ -12,7 +12,6 @@ public class EntersBattlefieldUntappedTriggeredAbility extends EntersBattlefield public EntersBattlefieldUntappedTriggeredAbility(Effect effect, boolean optional) { super(effect, optional); - this.ignoreRulesGeneration = true; setTriggerPhrase("When {this} enters the battlefield untapped, "); } @@ -33,4 +32,4 @@ public class EntersBattlefieldUntappedTriggeredAbility extends EntersBattlefield Permanent permanent = game.getPermanent(event.getTargetId()); return permanent != null && !permanent.isTapped(); } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/abilities/common/SourceBecomesTargetTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SourceBecomesTargetTriggeredAbility.java deleted file mode 100644 index bb0c7ebb592..00000000000 --- a/Mage/src/main/java/mage/abilities/common/SourceBecomesTargetTriggeredAbility.java +++ /dev/null @@ -1,80 +0,0 @@ -package mage.abilities.common; - -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.constants.SetTargetPointer; -import mage.constants.Zone; -import mage.filter.FilterStackObject; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.StackObject; -import mage.target.targetpointer.FixedTarget; - -/** - * @author North - */ -public class SourceBecomesTargetTriggeredAbility extends TriggeredAbilityImpl { - - private final FilterStackObject filter; - private final SetTargetPointer setTargetPointer; - - public SourceBecomesTargetTriggeredAbility(Effect effect) { - this(effect, StaticFilters.FILTER_SPELL_OR_ABILITY_A); - } - - public SourceBecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter) { - this(effect, filter, SetTargetPointer.NONE); - } - - public SourceBecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer) { - this(effect, filter, setTargetPointer, false); - } - - public SourceBecomesTargetTriggeredAbility(Effect effect, FilterStackObject filter, SetTargetPointer setTargetPointer, boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); - this.filter = filter; - this.setTargetPointer = setTargetPointer; - setTriggerPhrase("When {this} becomes the target of " + filter.getMessage() + ", "); - } - - protected SourceBecomesTargetTriggeredAbility(final SourceBecomesTargetTriggeredAbility ability) { - super(ability); - this.filter = ability.filter; - this.setTargetPointer = ability.setTargetPointer; - } - - @Override - public SourceBecomesTargetTriggeredAbility copy() { - return new SourceBecomesTargetTriggeredAbility(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 (!event.getTargetId().equals(getSourceId()) - || !filter.match(sourceObject, getControllerId(), this, game)) { - return false; - } - switch (setTargetPointer) { - case PLAYER: - this.getEffects().stream() - .forEach(effect -> effect.setTargetPointer( - new FixedTarget(sourceObject.getControllerId(), game) - )); - break; - case SPELL: - this.getEffects().stream() - .forEach(effect -> effect.setTargetPointer( - new FixedTarget(sourceObject.getId(), game) - )); - break; - } - return true; - } -} diff --git a/Mage/src/main/java/mage/abilities/common/TargetOfOpponentsSpellOrAbilityTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TargetOfOpponentsSpellOrAbilityTriggeredAbility.java deleted file mode 100644 index 29bf7131c98..00000000000 --- a/Mage/src/main/java/mage/abilities/common/TargetOfOpponentsSpellOrAbilityTriggeredAbility.java +++ /dev/null @@ -1,82 +0,0 @@ -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.players.Player; -import mage.target.targetpointer.FixedTarget; - -/** - * "Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls," - * AND - * "Whenever you become the target of a spell or ability an opponent controls," - * - * @author Alex-Vasile - */ -public class TargetOfOpponentsSpellOrAbilityTriggeredAbility extends TriggeredAbilityImpl { - - private boolean onlyController = false; - - public TargetOfOpponentsSpellOrAbilityTriggeredAbility(Effect effect) { - this(effect, false, false); - } - - public TargetOfOpponentsSpellOrAbilityTriggeredAbility(Effect effect, boolean optional, boolean onlyController) { - super(Zone.BATTLEFIELD, effect, optional); - this.onlyController = onlyController; - if (this.onlyController) { - setTriggerPhrase("Whenever you become the target of a spell or ability an opponent controls, "); - } else { - setTriggerPhrase("Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, "); - } - } - - private TargetOfOpponentsSpellOrAbilityTriggeredAbility(final TargetOfOpponentsSpellOrAbilityTriggeredAbility ability) { - super(ability); - this.onlyController = ability.onlyController; - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Player controller = game.getPlayer(this.getControllerId()); - Player targetter = game.getPlayer(event.getPlayerId()); - if (controller == null || targetter == null || controller.getId().equals(targetter.getId())) { - return false; - } - - // Check if player was targeted - if (controller.getId().equals(event.getTargetId())) { - // Add target for effects which need it (e.g. the counter effect from AmuletOfSafekeeping) - this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId())); - return true; - } - - // If only the controller is - if (this.onlyController) { - return false; - } - - // Check if permanent was targeted - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent == null || !controller.getId().equals(permanent.getControllerId())) { - return false; - } - - // Add target for effects which need it (e.g. the counter effect from AmuletOfSafekeeping) - this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId())); - return true; - } - - @Override - public TargetOfOpponentsSpellOrAbilityTriggeredAbility copy() { - return new TargetOfOpponentsSpellOrAbilityTriggeredAbility(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/common/TurnedFaceUpSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/TurnedFaceUpSourceTriggeredAbility.java index 58967ad8a96..145ca173a35 100644 --- a/Mage/src/main/java/mage/abilities/common/TurnedFaceUpSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/TurnedFaceUpSourceTriggeredAbility.java @@ -5,7 +5,6 @@ import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.targetpointer.FixedTarget; /** @@ -14,7 +13,7 @@ import mage.target.targetpointer.FixedTarget; public class TurnedFaceUpSourceTriggeredAbility extends TriggeredAbilityImpl { - private boolean setTargetPointer; + private final boolean setTargetPointer; public TurnedFaceUpSourceTriggeredAbility(Effect effect) { this(effect, false); @@ -30,6 +29,7 @@ public class TurnedFaceUpSourceTriggeredAbility extends TriggeredAbilityImpl { this.setWorksFaceDown(true); this.setTargetPointer = setTargetPointer; setTriggerPhrase("When {this} is turned face up, "); + this.replaceRuleText = true; } protected TurnedFaceUpSourceTriggeredAbility(final TurnedFaceUpSourceTriggeredAbility 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 eca3a8a38dd..1a3f07a3e2a 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/AttachedToMatchesFilterCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/AttachedToMatchesFilterCondition.java @@ -19,6 +19,9 @@ public class AttachedToMatchesFilterCondition implements Condition { public AttachedToMatchesFilterCondition(FilterPermanent filter) { this.filter = filter; + if (filter == null) { + throw new IllegalStateException("Wrong code usage. Filter must be non-nullable."); + } } @Override diff --git a/Mage/src/main/java/mage/abilities/condition/common/DefendingPlayerControlsNoSourceCondition.java b/Mage/src/main/java/mage/abilities/condition/common/DefendingPlayerControlsNoSourceCondition.java index afe26d7a571..d9866ab633f 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/DefendingPlayerControlsNoSourceCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/DefendingPlayerControlsNoSourceCondition.java @@ -10,6 +10,10 @@ import mage.game.Game; /** * This condition always returns false outside of the combat phase. * + * The name is bad, but "No Source" is refering to the fact this is to be used outside + * a "Whenever ~ attacks," or similar, where the "defending player" takes another + * meaning. {@link DefendingPlayerControlsNoSourcePredicate} for more info. + * * @author Susucr */ public class DefendingPlayerControlsNoSourceCondition implements Condition { diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousRuleModifyingEffect.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousRuleModifyingEffect.java index 00f553952b9..83d74dcd3c0 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousRuleModifyingEffect.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalContinuousRuleModifyingEffect.java @@ -66,11 +66,6 @@ public class ConditionalContinuousRuleModifyingEffect extends ContinuousRuleModi return effect.isDiscarded() || (otherwiseEffect != null && otherwiseEffect.isDiscarded()); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public boolean checksEventType(GameEvent event, Game game) { if (effect.checksEventType(event, game)) { diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousRuleModifyingEffect.java b/Mage/src/main/java/mage/abilities/effects/ContinuousRuleModifyingEffect.java index 5dd27c2f644..7442cf947b4 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousRuleModifyingEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousRuleModifyingEffect.java @@ -1,5 +1,3 @@ - - package mage.abilities.effects; import mage.abilities.Ability; diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousRuleModifyingEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/ContinuousRuleModifyingEffectImpl.java index 8be834d8a30..3e07c9be11f 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousRuleModifyingEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousRuleModifyingEffectImpl.java @@ -59,24 +59,11 @@ public abstract class ContinuousRuleModifyingEffectImpl extends ContinuousEffect this.messageToGameLog = effect.messageToGameLog; } - /** - * An early check for the event types this effect applies to. This check was - * added to speed up event handling. Once all existing - * ContinuousRuleModifiyingEffects have implemented this method, the method - * should be changed to abstract here or removed. - * - * @param event - * @param game - * @return - */ @Override - public boolean checksEventType(GameEvent event, Game game) { - return true; - } - - @Override - public boolean apply(Game game, Ability source) { - return true; + final public boolean apply(Game game, Ability source) { + // not used in rule modifying effects because it allows or disallows related event only without data + // modification or choose dialogs (see applies method) + throw new IllegalStateException("Wrong code usage. Rules modifying effects don't use apply methods"); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantActivateAbilitiesAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantActivateAbilitiesAttachedEffect.java index b6f4d6f113c..129b2f2cff3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantActivateAbilitiesAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantActivateAbilitiesAttachedEffect.java @@ -29,11 +29,6 @@ public class CantActivateAbilitiesAttachedEffect extends ContinuousRuleModifying return new CantActivateAbilitiesAttachedEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ACTIVATE_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 485d0ef92f1..f050ad42afa 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredControlledEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredControlledEffect.java @@ -45,11 +45,6 @@ public class CantBeCounteredControlledEffect extends ContinuousRuleModifyingEffe return new CantBeCounteredControlledEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.COUNTER; diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredSourceEffect.java index edcf056da3d..4d9f3514c6f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredSourceEffect.java @@ -32,11 +32,6 @@ public class CantBeCounteredSourceEffect extends ContinuousRuleModifyingEffectIm return new CantBeCounteredSourceEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { StackObject stackObject = game.getStack().getStackObject(event.getTargetId()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedSourceEffect.java index e2e9a5f8860..f75377d228a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedSourceEffect.java @@ -31,11 +31,6 @@ public class CantBeRegeneratedSourceEffect extends ContinuousRuleModifyingEffect return new CantBeRegeneratedSourceEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.REGENERATE; diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedTargetEffect.java index cbd61f26cd2..16c303ee20b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeRegeneratedTargetEffect.java @@ -27,11 +27,6 @@ public class CantBeRegeneratedTargetEffect extends ContinuousRuleModifyingEffect return new CantBeRegeneratedTargetEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.REGENERATE; 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 cdbdef87ce9..9f671dfa22f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java @@ -49,11 +49,6 @@ public class CantBeTargetedAllEffect extends ContinuousRuleModifyingEffectImpl { return new CantBeTargetedAllEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.TARGET; diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAttachedEffect.java index 7893f2de689..f2ea7b39a40 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAttachedEffect.java @@ -44,11 +44,6 @@ public class CantBeTargetedAttachedEffect extends ContinuousRuleModifyingEffectI return new CantBeTargetedAttachedEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.TARGET; diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedCardsGraveyardsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedCardsGraveyardsEffect.java index d1f9720f65f..b81439d3f2b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedCardsGraveyardsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedCardsGraveyardsEffect.java @@ -30,11 +30,6 @@ public class CantBeTargetedCardsGraveyardsEffect extends ContinuousRuleModifying return new CantBeTargetedCardsGraveyardsEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.TARGET; @@ -46,7 +41,7 @@ public class CantBeTargetedCardsGraveyardsEffect extends ContinuousRuleModifying StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); if (targetCard != null && stackObject != null) { Zone zone = game.getState().getZone(targetCard.getId()); - if (zone != null && zone == Zone.GRAVEYARD) { + if (zone == Zone.GRAVEYARD) { 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 3f76d6976f1..7a6069d6b73 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedSourceEffect.java @@ -36,11 +36,6 @@ public class CantBeTargetedSourceEffect extends ContinuousRuleModifyingEffectImp return new CantBeTargetedSourceEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.TARGET; 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 6077fae25c3..4ce8e4b36ca 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedTargetEffect.java @@ -39,11 +39,6 @@ public class CantBeTargetedTargetEffect extends ContinuousRuleModifyingEffectImp return new CantBeTargetedTargetEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.TARGET; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseModeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseModeEffect.java index 4747a19f6b5..d01a7ada574 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChooseModeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseModeEffect.java @@ -47,7 +47,7 @@ public class ChooseModeEffect extends OneShotEffect { if (sourcePermanent == null) { sourcePermanent = game.getPermanentEntering(source.getSourceId()); } - if (controller != null) { + if (controller != null && sourcePermanent != null) { Choice choice = new ChoiceImpl(true); choice.setMessage(choiceMessage); choice.getChoices().addAll(modes); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java index 6435bab08da..44c1530d9be 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -40,7 +40,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { private final List addedTokenPermanents; private final List additionalAbilities; private final CardType additionalCardType; - private SubType additionalSubType; + private final List additionalSubTypes = new ArrayList<>(); private final UUID attackedPlayer; private UUID attachedTo = null; private final boolean attacking; @@ -133,7 +133,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { this.addedTokenPermanents = new ArrayList<>(effect.addedTokenPermanents); this.additionalAbilities = new ArrayList<>(effect.additionalAbilities); this.additionalCardType = effect.additionalCardType; - this.additionalSubType = effect.additionalSubType; + this.additionalSubTypes.addAll(effect.additionalSubTypes); this.attackedPlayer = effect.attackedPlayer; this.attachedTo = effect.attachedTo; this.attacking = effect.attacking; @@ -236,8 +236,10 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { token.removeAllCreatureTypes(); token.addSubType(onlySubType); } - if (additionalSubType != null) { - token.addSubType(additionalSubType); + if (!additionalSubTypes.isEmpty()) { + for (SubType additionalSubType : additionalSubTypes) { + token.addSubType(additionalSubType); + } } if (color != null) { token.setColor(color); @@ -322,8 +324,8 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { return addedTokenPermanents; } - public CreateTokenCopyTargetEffect setAdditionalSubType(SubType additionalSubType) { - this.additionalSubType = additionalSubType; + public CreateTokenCopyTargetEffect withAdditionalSubType(SubType additionalSubType) { + this.additionalSubTypes.add(additionalSubType); return this; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DamageAttachedControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DamageAttachedControllerEffect.java index ac85b90d186..702aef678c7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DamageAttachedControllerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DamageAttachedControllerEffect.java @@ -1,12 +1,10 @@ package mage.abilities.effects.common; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; 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; @@ -40,12 +38,7 @@ public class DamageAttachedControllerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment == null) { return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DamageAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DamageAttachedEffect.java index 4047bd3f334..142cfb19028 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DamageAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DamageAttachedEffect.java @@ -1,12 +1,10 @@ package mage.abilities.effects.common; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; @@ -47,12 +45,7 @@ public class DamageAttachedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment == null) { return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersNextUntapStepSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersNextUntapStepSourceEffect.java index 94f1bb65aae..7612d72c83e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersNextUntapStepSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersNextUntapStepSourceEffect.java @@ -28,11 +28,6 @@ public class DontUntapInControllersNextUntapStepSourceEffect extends ContinuousR return new DontUntapInControllersNextUntapStepSourceEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -44,7 +39,8 @@ public class DontUntapInControllersNextUntapStepSourceEffect extends ContinuousR @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UNTAP_STEP || event.getType() == GameEvent.EventType.UNTAP; + return event.getType() == GameEvent.EventType.UNTAP_STEP + || event.getType() == GameEvent.EventType.UNTAP; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersNextUntapStepTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersNextUntapStepTargetEffect.java index 92ce682a732..e0ca981deb8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersNextUntapStepTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersNextUntapStepTargetEffect.java @@ -71,11 +71,6 @@ public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousR return new DontUntapInControllersNextUntapStepTargetEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -88,7 +83,8 @@ public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousR @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UNTAP_STEP || event.getType() == GameEvent.EventType.UNTAP; + return event.getType() == GameEvent.EventType.UNTAP_STEP + || event.getType() == GameEvent.EventType.UNTAP; } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepAllEffect.java index 3b109db119e..95754f096ff 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepAllEffect.java @@ -41,11 +41,6 @@ public class DontUntapInControllersUntapStepAllEffect extends ContinuousRuleModi return new DontUntapInControllersUntapStepAllEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.UNTAP; diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepEnchantedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepEnchantedEffect.java index 6623974ca01..eb67d9d755d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepEnchantedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepEnchantedEffect.java @@ -27,11 +27,6 @@ public class DontUntapInControllersUntapStepEnchantedEffect extends ContinuousRu super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public DontUntapInControllersUntapStepEnchantedEffect copy() { return new DontUntapInControllersUntapStepEnchantedEffect(this); diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepSourceEffect.java index c235ef7b1f6..62b0f9b1b03 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepSourceEffect.java @@ -34,11 +34,6 @@ public class DontUntapInControllersUntapStepSourceEffect extends ContinuousRuleM return new DontUntapInControllersUntapStepSourceEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.UNTAP; diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java index 01c0b7adc9f..e9c369720fa 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInControllersUntapStepTargetEffect.java @@ -38,11 +38,6 @@ public class DontUntapInControllersUntapStepTargetEffect extends ContinuousRuleM return new DontUntapInControllersUntapStepTargetEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.UNTAP; diff --git a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInPlayersNextUntapStepAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInPlayersNextUntapStepAllEffect.java index 8dd2566db8a..d24fe72ab5d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DontUntapInPlayersNextUntapStepAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DontUntapInPlayersNextUntapStepAllEffect.java @@ -46,11 +46,6 @@ public class DontUntapInPlayersNextUntapStepAllEffect extends ContinuousRuleModi return new DontUntapInPlayersNextUntapStepAllEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); @@ -63,7 +58,8 @@ public class DontUntapInPlayersNextUntapStepAllEffect extends ContinuousRuleModi @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UNTAP_STEP || event.getType() == GameEvent.EventType.UNTAP; + return event.getType() == GameEvent.EventType.UNTAP_STEP + || event.getType() == GameEvent.EventType.UNTAP; } @Override 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 95b1f92a50f..00ac482a536 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/EpicEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/EpicEffect.java @@ -75,11 +75,6 @@ class EpicReplacementEffect extends ContinuousRuleModifyingEffectImpl { super(effect); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public EpicReplacementEffect copy() { return new EpicReplacementEffect(this); 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 202158dd436..a4116c0d23b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileAttachedEffect.java @@ -3,7 +3,6 @@ package mage.abilities.effects.common; 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; @@ -31,12 +30,7 @@ public class ExileAttachedEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); // The LKI must be used for this step. 608.2g - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (controller != null && enchantment != null && enchantment.getAttachedTo() != null) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/PhaseOutAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PhaseOutAttachedEffect.java index ca4d540d768..1823bca9589 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PhaseOutAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PhaseOutAttachedEffect.java @@ -3,7 +3,6 @@ package mage.abilities.effects.common; 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; @@ -28,12 +27,7 @@ public class PhaseOutAttachedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - // In the case that the enchantment is blinked - Permanent enchantment = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (enchantment == null) { - // It was not blinked, use the standard method - enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId()); - } + Permanent enchantment = source.getSourcePermanentOrLKI(game); if (enchantment != null) { Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); if (enchanted != null) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandTargetEffect.java index 255a65d35a7..a6cd2c97f41 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandTargetEffect.java @@ -10,8 +10,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; -import mage.target.Target; -import mage.util.CardUtil; import java.util.*; @@ -63,7 +61,7 @@ public class ReturnToHandTargetEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } - return "return " + getTargetPointer().describeTargets(mode.getTargets(), "") + + return "return " + getTargetPointer().describeTargets(mode.getTargets(), "that creature") + (getTargetPointer().isPlural(mode.getTargets()) ? " to their owners' hands" : " to its owner's hand"); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/SkipUntapStepEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SkipUntapStepEffect.java index 9f8c312391d..444860dbff7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SkipUntapStepEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SkipUntapStepEffect.java @@ -58,11 +58,15 @@ public class SkipUntapStepEffect extends ContinuousRuleModifyingEffectImpl { return new SkipUntapStepEffect(this); } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UNTAP_STEP; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); - return event.getType() == GameEvent.EventType.UNTAP_STEP - && controller != null + return controller != null && game.getState().getPlayersInRange(controller.getId(), game).contains(event.getPlayerId()); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/TapEnchantedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TapEnchantedEffect.java index 44ebb13eed9..5e2f862633f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/TapEnchantedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/TapEnchantedEffect.java @@ -26,7 +26,7 @@ public class TapEnchantedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = source.getSourcePermanentIfItStillExists(game); + Permanent permanent = source.getSourcePermanentOrLKI(game); if (permanent != null) { Permanent attach = game.getPermanent(permanent.getAttachedTo()); if (attach != null) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/UntapAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/UntapAttachedEffect.java index b80597e3f03..c863da2ff66 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/UntapAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/UntapAttachedEffect.java @@ -28,7 +28,7 @@ public class UntapAttachedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); + Permanent permanent = source.getSourcePermanentOrLKI(game); if (permanent != null) { Permanent attach = game.getPermanent(permanent.getAttachedTo()); if (attach != null) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/ChooseBlockersEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/ChooseBlockersEffect.java index 07d1dfee53b..93e8d175159 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/ChooseBlockersEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/ChooseBlockersEffect.java @@ -28,11 +28,6 @@ public class ChooseBlockersEffect extends ContinuousRuleModifyingEffectImpl { return new ChooseBlockersEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DECLARING_BLOCKERS; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CantCastMoreThanOneSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CantCastMoreThanOneSpellEffect.java index 95432a435f2..44a35bfcc68 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CantCastMoreThanOneSpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CantCastMoreThanOneSpellEffect.java @@ -33,11 +33,6 @@ public class CantCastMoreThanOneSpellEffect extends ContinuousRuleModifyingEffec return new CantCastMoreThanOneSpellEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/DamageCantBePreventedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/DamageCantBePreventedEffect.java index 65c06b00ed7..9f894641458 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/DamageCantBePreventedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/DamageCantBePreventedEffect.java @@ -23,11 +23,6 @@ public class DamageCantBePreventedEffect extends ContinuousRuleModifyingEffectIm return new DamageCantBePreventedEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.PREVENT_DAMAGE; 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 9e41a785f96..3549d825d8c 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 @@ -12,8 +12,6 @@ import mage.constants.SubLayer; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.Target; -import mage.util.CardUtil; import java.util.UUID; @@ -128,31 +126,10 @@ public class GainControlTargetEffect extends ContinuousEffectImpl { @Override public String getText(Mode mode) { - if (!staticText.isEmpty()) { + if (staticText != null && !staticText.isEmpty()) { return staticText; } - - if (mode.getTargets().isEmpty()) { - return "gain control of target permanent"; - } - - Target target = mode.getTargets().get(0); - StringBuilder sb = new StringBuilder("gain control of "); - if (target.getMaxNumberOfTargets() > 1) { - if (target.getMinNumberOfTargets() == 0) { - sb.append("up to "); - } - sb.append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(" target "); - } else if (!target.getTargetName().startsWith("another")) { - if (target.getMinNumberOfTargets() == 0) { - sb.append("up to one "); - } - sb.append("target "); - } - sb.append(mode.getTargets().get(0).getTargetName()); - if (!duration.toString().isEmpty() && duration != Duration.EndOfGame) { - sb.append(' ').append(duration.toString()); - } - return sb.toString(); + return "gain control of " + getTargetPointer().describeTargets(mode.getTargets(), "that creature") + + (duration.toString().isEmpty() || duration == Duration.EndOfGame ? "" : " " + duration.toString()); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CantCastOrActivateOpponentsYourTurnEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CantCastOrActivateOpponentsYourTurnEffect.java index b1f6dfc6114..efc604e6cf8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CantCastOrActivateOpponentsYourTurnEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CantCastOrActivateOpponentsYourTurnEffect.java @@ -30,11 +30,6 @@ public class CantCastOrActivateOpponentsYourTurnEffect extends ContinuousRuleMod return new CantCastOrActivateOpponentsYourTurnEffect(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()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CantRegenerateTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CantRegenerateTargetEffect.java index 44a048110a5..cdc1711fc93 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CantRegenerateTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CantRegenerateTargetEffect.java @@ -32,11 +32,6 @@ public class CantRegenerateTargetEffect extends ContinuousRuleModifyingEffectImp return new CantRegenerateTargetEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.REGENERATE; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CastOnlyIfYouHaveCastAnotherSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CastOnlyIfYouHaveCastAnotherSpellEffect.java index 7c27be2442d..9a0289dbfe5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CastOnlyIfYouHaveCastAnotherSpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CastOnlyIfYouHaveCastAnotherSpellEffect.java @@ -39,11 +39,6 @@ public class CastOnlyIfYouHaveCastAnotherSpellEffect extends ContinuousRuleModif return false; } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public CastOnlyIfYouHaveCastAnotherSpellEffect copy() { return new CastOnlyIfYouHaveCastAnotherSpellEffect(this); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CombatDamageByToughnessAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CombatDamageByToughnessAllEffect.java index c262431ea82..3322d3ce9da 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CombatDamageByToughnessAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CombatDamageByToughnessAllEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common.ruleModifying; import mage.abilities.Ability; @@ -7,17 +6,25 @@ import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; /** - * @author TheElk801 + * @author TheElk801, xanderhall */ public class CombatDamageByToughnessAllEffect extends ContinuousEffectImpl { private final FilterCreaturePermanent filter; + public CombatDamageByToughnessAllEffect() { + this(StaticFilters.FILTER_PERMANENT_CREATURE); + } + /** + * "Each [] assigns combat damage equal to its toughness rather than its power" + * @param filter Warning: ObjectSourcePlayer predicates will be ignored + */ public CombatDamageByToughnessAllEffect(FilterCreaturePermanent filter) { this(filter, Duration.WhileOnBattlefield); } @@ -25,9 +32,8 @@ public class CombatDamageByToughnessAllEffect extends ContinuousEffectImpl { public CombatDamageByToughnessAllEffect(FilterCreaturePermanent filter, Duration duration) { super(duration, Layer.RulesEffects, SubLayer.NA, Outcome.Neutral); this.filter = filter; - this.staticText = filter.getMessage() + " assigns combat damage equal to its toughness rather than its power"; + this.staticText = "each " + filter.getMessage() + " assigns combat damage equal to its toughness rather than its power"; } - private CombatDamageByToughnessAllEffect(final CombatDamageByToughnessAllEffect effect) { super(effect); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CombatDamageByToughnessControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CombatDamageByToughnessControlledEffect.java new file mode 100644 index 00000000000..d11473e1549 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CombatDamageByToughnessControlledEffect.java @@ -0,0 +1,59 @@ +package mage.abilities.effects.common.ruleModifying; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; + +/** + * @author TheElk801, xenohedron + */ +public class CombatDamageByToughnessControlledEffect extends ContinuousEffectImpl { + + private final FilterCreaturePermanent filter; + + public CombatDamageByToughnessControlledEffect() { + this(StaticFilters.FILTER_PERMANENT_CREATURE); + } + + /** + * "Each [] you control assigns combat damage equal to its toughness rather than its power" + * @param filter Warning: ObjectSourcePlayer predicates will be ignored + */ + public CombatDamageByToughnessControlledEffect(FilterCreaturePermanent filter) { + this(filter, Duration.WhileOnBattlefield); + } + + public CombatDamageByToughnessControlledEffect(FilterCreaturePermanent filter, Duration duration) { + super(duration, Layer.RulesEffects, SubLayer.NA, Outcome.Neutral); + this.filter = filter; + this.staticText = "each " + filter.getMessage() + + (filter.getMessage().contains("you control") ? "" : " you control") + + " assigns combat damage equal to its toughness rather than its power"; + } + + private CombatDamageByToughnessControlledEffect(final CombatDamageByToughnessControlledEffect effect) { + super(effect); + this.filter = effect.filter; + } + + @Override + public CombatDamageByToughnessControlledEffect copy() { + return new CombatDamageByToughnessControlledEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + FilterCreaturePermanent filterPermanent = filter.copy(); + filterPermanent.add(new ControllerIdPredicate(source.getControllerId())); + game.getCombat().setUseToughnessForDamage(true); + game.getCombat().addUseToughnessForDamageFilter(filterPermanent); + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/AftermathAbility.java b/Mage/src/main/java/mage/abilities/keyword/AftermathAbility.java index e2232b11e33..75af466f270 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AftermathAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AftermathAbility.java @@ -104,11 +104,6 @@ class AftermathCantCastFromHand extends ContinuousRuleModifyingEffectImpl { return new AftermathCantCastFromHand(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.CAST_SPELL; diff --git a/Mage/src/main/java/mage/abilities/keyword/FabricateAbility.java b/Mage/src/main/java/mage/abilities/keyword/FabricateAbility.java index ab6b2beeb48..3ed2848c066 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FabricateAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FabricateAbility.java @@ -21,7 +21,7 @@ public class FabricateAbility extends EntersBattlefieldTriggeredAbility { private final int value; public FabricateAbility(int value) { - super(new FabricateEffect(value), false, true); + super(new FabricateEffect(value), false); this.value = value; } diff --git a/Mage/src/main/java/mage/abilities/keyword/SplitSecondAbility.java b/Mage/src/main/java/mage/abilities/keyword/SplitSecondAbility.java index de3a7c95e63..7a2e7829029 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SplitSecondAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SplitSecondAbility.java @@ -59,7 +59,8 @@ class SplitSecondEffect extends ContinuousRuleModifyingEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.CAST_SPELL || event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; + return event.getType() == GameEvent.EventType.CAST_SPELL + || event.getType() == GameEvent.EventType.ACTIVATE_ABILITY; } @Override @@ -76,11 +77,6 @@ class SplitSecondEffect extends ContinuousRuleModifyingEffectImpl { return false; } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public SplitSecondEffect copy() { return new SplitSecondEffect(this); diff --git a/Mage/src/main/java/mage/abilities/keyword/WardAbility.java b/Mage/src/main/java/mage/abilities/keyword/WardAbility.java index 03a4c667362..604828361f2 100644 --- a/Mage/src/main/java/mage/abilities/keyword/WardAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/WardAbility.java @@ -11,7 +11,6 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.StackObject; -import mage.target.Target; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; @@ -22,7 +21,6 @@ public class WardAbility extends TriggeredAbilityImpl { private final Cost cost; private final DynamicValue genericMana; - private final boolean showAbilityHint; private final String whereXIs; @@ -74,29 +72,19 @@ public class WardAbility extends TriggeredAbilityImpl { return event.getType() == GameEvent.EventType.TARGETED; } - private StackObject getTargetingObject(GameEvent event, Game game) { - for (StackObject stackObject : game.getStack()) { - if (stackObject.getId().equals(event.getSourceId()) || stackObject.getSourceId().equals(event.getSourceId())) { - for (Target target : stackObject.getStackAbility().getTargets()) { - if (target.contains(getSourceId())) { - return stackObject; - } - } - } - } - return null; - } - @Override public boolean checkTrigger(GameEvent event, Game game) { if (!getSourceId().equals(event.getTargetId())) { return false; } - StackObject stackObject = getTargetingObject(event, game); - if (stackObject == null || !game.getOpponents(getControllerId()).contains(stackObject.getControllerId())) { + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || !game.getOpponents(getControllerId()).contains(targetingObject.getControllerId())) { return false; } - getEffects().setTargetPointer(new FixedTarget(stackObject.getId(), game)); + if (CardUtil.checkTargetedEventAlreadyUsed(this.id.toString(), targetingObject, event, game)) { + return false; + } + getEffects().setTargetPointer(new FixedTarget(targetingObject.getId())); return true; } diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorCardInYourGraveyardManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorCardInYourGraveyardManaAbility.java index 85801b0b3dd..19dab086f6a 100644 --- a/Mage/src/main/java/mage/abilities/mana/AnyColorCardInYourGraveyardManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/AnyColorCardInYourGraveyardManaAbility.java @@ -8,6 +8,7 @@ import mage.abilities.effects.mana.ManaEffect; import mage.cards.Card; import mage.choices.Choice; import mage.constants.ManaType; +import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterCard; @@ -88,7 +89,7 @@ class AnyColorCardInYourGraveyardManaEffect extends ManaEffect { choice.setChoice(choice.getChoices().iterator().next()); } else { Player player = game.getPlayer(source.getControllerId()); - if (player == null || !player.choose(outcome, choice, game)) { + if (player == null || !player.choose(Outcome.PutManaInPool, choice, game)) { return null; } } diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java index 92f572f0975..9c3b8f26943 100644 --- a/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/AnyColorLandsProduceManaAbility.java @@ -6,6 +6,7 @@ import mage.abilities.Ability; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.mana.ManaEffect; import mage.choices.Choice; +import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterPermanent; @@ -117,7 +118,7 @@ class AnyColorLandsProduceManaEffect extends ManaEffect { choice.setChoice(choice.getChoices().iterator().next()); } else { Player player = game.getPlayer(source.getControllerId()); - if (player == null || !player.choose(outcome, choice, game)) { + if (player == null || !player.choose(Outcome.PutManaInPool, choice, game)) { return null; } } diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java index 0d9b0cebaeb..f6cd43bbfcc 100644 --- a/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java @@ -7,6 +7,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.mana.ManaEffect; import mage.choices.Choice; import mage.constants.ManaType; +import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterPermanent; @@ -90,7 +91,7 @@ class AnyColorPermanentTypesManaEffect extends ManaEffect { choice.setChoice(choice.getChoices().iterator().next()); } else { Player player = game.getPlayer(source.getControllerId()); - if (player == null || !player.choose(outcome, choice, game)) { + if (player == null || !player.choose(Outcome.PutManaInPool, choice, game)) { return null; } } diff --git a/Mage/src/main/java/mage/abilities/mana/CommanderColorIdentityManaAbility.java b/Mage/src/main/java/mage/abilities/mana/CommanderColorIdentityManaAbility.java index d6489776937..57d5aa40784 100644 --- a/Mage/src/main/java/mage/abilities/mana/CommanderColorIdentityManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/CommanderColorIdentityManaAbility.java @@ -10,6 +10,7 @@ import mage.choices.Choice; import mage.choices.ChoiceImpl; import mage.constants.ColoredManaSymbol; import mage.constants.CommanderCardType; +import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterMana; import mage.game.Game; @@ -132,7 +133,7 @@ class CommanderIdentityManaEffect extends ManaEffect { if (choice.getChoices().size() == 1) { choice.setChoice(choice.getChoices().iterator().next()); } else { - if (!controller.choose(outcome, choice, game)) { + if (!controller.choose(Outcome.PutManaInPool, choice, game)) { return mana; } } diff --git a/Mage/src/main/java/mage/constants/MatchBufferTime.java b/Mage/src/main/java/mage/constants/MatchBufferTime.java index bfb93e2ca5e..26714b2cc24 100644 --- a/Mage/src/main/java/mage/constants/MatchBufferTime.java +++ b/Mage/src/main/java/mage/constants/MatchBufferTime.java @@ -11,6 +11,9 @@ package mage.constants; */ public enum MatchBufferTime { NONE(0, "None"), + SEC__01(1, "1 Second"), + SEC__02(2, "2 Seconds"), + SEC__03(3, "3 Seconds"), SEC__05(5, "5 Seconds"), SEC__10(10, "10 Seconds"), SEC__15(15, "15 Seconds"), diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index 7c9e88388aa..3094d079006 100644 --- a/Mage/src/main/java/mage/filter/StaticFilters.java +++ b/Mage/src/main/java/mage/filter/StaticFilters.java @@ -1229,10 +1229,4 @@ public final class StaticFilters { FILTER_CONTROLLED_FOOD.setLockedFilter(true); } - public static final FilterCreaturePermanent FILTER_CONTROLLED_CREATURE_EACH = new FilterCreaturePermanent("each creature you control"); - - static { - FILTER_CONTROLLED_CREATURE_EACH.add(TargetController.YOU.getPlayerPredicate()); - FILTER_CONTROLLED_CREATURE_EACH.setLockedFilter(true); - } } diff --git a/Mage/src/main/java/mage/filter/predicate/card/ExpansionSetPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/ExpansionSetPredicate.java index 6afe314eed2..96087bd4563 100644 --- a/Mage/src/main/java/mage/filter/predicate/card/ExpansionSetPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/card/ExpansionSetPredicate.java @@ -13,6 +13,10 @@ public class ExpansionSetPredicate implements Predicate { private final String setCode; + /** + * Per CR 206.1, the expansion symbol has no effect on game play. + * Use only for utility or silver-bordered / acorn mechanics. + */ public ExpansionSetPredicate(String setCode) { this.setCode = setCode; } diff --git a/Mage/src/main/java/mage/filter/predicate/other/AuraSpellPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/AuraSpellPredicate.java new file mode 100644 index 00000000000..dea183699c1 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/other/AuraSpellPredicate.java @@ -0,0 +1,31 @@ +package mage.filter.predicate.other; + +import mage.abilities.SpellAbility; +import mage.constants.SubType; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; + +/** + * Needed for "becomes the target of an Aura spell" to work correctly with e.g. Disturb, Bestow + * + * @author xenohedron + */ +public enum AuraSpellPredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject input, Game game) { + if (!(input instanceof Spell)) { + return false; + } + SpellAbility spellAbility = ((Spell) input).getSpellAbility(); + return spellAbility != null && spellAbility.getCharacteristics(game).hasSubtype(SubType.AURA, game); + } + + @Override + public String toString() { + return "an Aura spell"; + } +} diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 17cb7312a11..275f3974007 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -22,6 +22,7 @@ import mage.abilities.mana.TriggeredManaAbility; import mage.actions.impl.MageAction; import mage.cards.*; import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardInfo; import mage.choices.Choice; import mage.constants.*; import mage.counters.CounterType; @@ -41,6 +42,7 @@ import mage.game.combat.Combat; import mage.game.combat.CombatGroup; import mage.game.command.*; import mage.game.command.dungeons.UndercityDungeon; +import mage.game.command.emblems.EmblemOfCard; import mage.game.command.emblems.TheRingEmblem; import mage.game.events.*; import mage.game.events.TableEvent.EventType; @@ -1317,6 +1319,22 @@ public abstract class GameImpl implements Game { addPlane(plane, startingPlayerId); state.setPlaneChase(this, gameOptions.planeChase); } + + if (!gameOptions.perPlayerEmblemCards.isEmpty()) { + for (UUID playerId : state.getPlayerList(startingPlayerId)) { + for (DeckCardInfo info : gameOptions.perPlayerEmblemCards) { + Card card = EmblemOfCard.cardFromDeckInfo(info); + addEmblem(new EmblemOfCard(card), card, playerId); + } + } + } + + if (!gameOptions.globalEmblemCards.isEmpty()) { + for (DeckCardInfo info : gameOptions.globalEmblemCards) { + Card card = EmblemOfCard.cardFromDeckInfo(info); + addEmblem(new EmblemOfCard(card), card, startingPlayerId); + } + } } public void initGameDefaultWatchers() { diff --git a/Mage/src/main/java/mage/game/GameOptions.java b/Mage/src/main/java/mage/game/GameOptions.java index 918b05fd6c9..f9fe54a7727 100644 --- a/Mage/src/main/java/mage/game/GameOptions.java +++ b/Mage/src/main/java/mage/game/GameOptions.java @@ -1,10 +1,13 @@ package mage.game; +import mage.cards.decks.DeckCardInfo; import mage.constants.PhaseStep; import mage.util.Copyable; import java.io.Serializable; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.Set; /** @@ -52,6 +55,15 @@ public class GameOptions implements Serializable, Copyable { */ public Set bannedUsers = Collections.emptySet(); + /** + * Cards to be given to each player as emblems + */ + public Collection perPlayerEmblemCards = Collections.emptySet(); + /** + * Cards to be given to the starting player as emblems + */ + public Collection globalEmblemCards = Collections.emptySet(); + // PLANECHASE game mode public boolean planeChase = false; @@ -73,6 +85,8 @@ public class GameOptions implements Serializable, Copyable { this.rollbackTurnsAllowed = options.rollbackTurnsAllowed; this.bannedUsers.addAll(options.bannedUsers); this.planeChase = options.planeChase; + this.perPlayerEmblemCards = new HashSet<>(options.perPlayerEmblemCards); + this.globalEmblemCards = new HashSet<>(options.globalEmblemCards); } @Override diff --git a/Mage/src/main/java/mage/game/command/Emblem.java b/Mage/src/main/java/mage/game/command/Emblem.java index 7ca863fd12c..b34bb6ba2f7 100644 --- a/Mage/src/main/java/mage/game/command/Emblem.java +++ b/Mage/src/main/java/mage/game/command/Emblem.java @@ -33,7 +33,7 @@ public abstract class Emblem extends CommandObjectImpl { private static final ManaCosts emptyCost = new ManaCostsImpl<>(); private UUID controllerId; - private MageObject sourceObject; + protected MageObject sourceObject; private boolean copy; private MageObject copyFrom; // copied card INFO (used to call original adjusters) private FrameStyle frameStyle; diff --git a/Mage/src/main/java/mage/game/command/emblems/EmblemOfCard.java b/Mage/src/main/java/mage/game/command/emblems/EmblemOfCard.java new file mode 100644 index 00000000000..74c16dbb2e2 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/EmblemOfCard.java @@ -0,0 +1,108 @@ +package mage.game.command.emblems; + +import mage.MageObject; +import mage.abilities.AbilityImpl; +import mage.cards.Card; +import mage.cards.decks.DeckCardInfo; +import mage.cards.mock.MockCard; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; +import mage.constants.Zone; +import mage.game.command.Emblem; +import mage.util.CardUtil; +import org.apache.log4j.Logger; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author artemiswkearney + * Emblem with all the abilities of an existing card. + * Can be used for custom gamemodes like Omniscience Draft (as seen on Arena), + * mana burn with Yurlok of Scorch Thrash, and anything else players might think of. + */ +public final class EmblemOfCard extends Emblem { + private final boolean usesVariousArt; + private static final Logger logger = Logger.getLogger(EmblemOfCard.class); + + public static Card lookupCard( + String cardName, + String cardNumber, + String setCode, + String infoTypeForError + ) { + int cardNumberInt = CardUtil.parseCardNumberAsInt(cardNumber); + List found = CardRepository.instance.findCards(new CardCriteria() + .name(cardName) + .minCardNumber(cardNumberInt) + .maxCardNumber(cardNumberInt) + .setCodes(setCode)); + return found.stream() + .filter(ci -> ci.getCardNumber().equals(cardNumber)) + .findFirst() + .orElseGet(() -> found.stream() + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("No real card for " + infoTypeForError + " " + cardName))) + .getCard(); + } + + public static Card cardFromDeckInfo(DeckCardInfo info) { + return lookupCard( + info.getCardName(), + info.getCardNum(), + info.getSetCode(), + "DeckCardInfo" + ); + } + + public EmblemOfCard(Card card, Zone zone) { + super(card.getName()); + if (card instanceof MockCard) { + card = lookupCard( + card.getName(), + card.getCardNumber(), + card.getExpansionSetCode(), + "MockCard" + ); + } + this.getAbilities().addAll(card.getAbilities().stream().filter( + ability -> zone.match(ability.getZone()) + ).map(ability -> { + if (ability instanceof AbilityImpl && ability.getZone() == zone) { + return ((AbilityImpl)ability).copyWithZone(Zone.COMMAND); + } + return ability; + }).collect(Collectors.toList())); + this.getAbilities().setSourceId(this.getId()); + this.setExpansionSetCode(card.getExpansionSetCode()); + this.setCardNumber(card.getCardNumber()); + this.setImageNumber(card.getImageNumber()); + this.usesVariousArt = card.getUsesVariousArt(); + } + + public EmblemOfCard(Card card) { + this(card, Zone.BATTLEFIELD); + } + + private EmblemOfCard(EmblemOfCard eoc) { + super(eoc); + this.usesVariousArt = eoc.usesVariousArt; + } + @Override + public EmblemOfCard copy() { + return new EmblemOfCard(this); + } + + @Override + public void setSourceObject(MageObject sourceObject) { + this.sourceObject = sourceObject; + // super method would try and fail to find the emblem image here + // (not sure why that would be setSoureObject's job; we get our image during construction) + } + + public boolean getUsesVariousArt() { + return usesVariousArt; + } +} + diff --git a/Mage/src/main/java/mage/game/command/emblems/GideonOfTheTrialsEmblem.java b/Mage/src/main/java/mage/game/command/emblems/GideonOfTheTrialsEmblem.java index d79270a9771..bc9107acd9e 100644 --- a/Mage/src/main/java/mage/game/command/emblems/GideonOfTheTrialsEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/GideonOfTheTrialsEmblem.java @@ -51,8 +51,9 @@ class GideonOfTheTrialsCantLoseEffect extends ContinuousRuleModifyingEffectImpl } @Override - public boolean apply(Game game, Ability source) { - return true; + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.WINS + || event.getType() == GameEvent.EventType.LOSES; } @Override diff --git a/Mage/src/main/java/mage/game/command/emblems/NarsetTranscendentEmblem.java b/Mage/src/main/java/mage/game/command/emblems/NarsetTranscendentEmblem.java index 171fd083d24..f66f3cef996 100644 --- a/Mage/src/main/java/mage/game/command/emblems/NarsetTranscendentEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/NarsetTranscendentEmblem.java @@ -52,11 +52,6 @@ class NarsetTranscendentCantCastEffect extends ContinuousRuleModifyingEffectImpl return new NarsetTranscendentCantCastEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public String getInfoMessage(Ability source, GameEvent event, Game game) { MageObject mageObject = game.getObject(source); 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 05c53bca4e2..f57e12b7a3b 100644 --- a/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/EdgeOfMalacolPlane.java @@ -84,11 +84,6 @@ class EdgeOfMalacolEffect extends ContinuousRuleModifyingEffectImpl { return new EdgeOfMalacolEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return false; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.UNTAP; diff --git a/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java b/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java index bc3cd1a4475..bda9c9d3181 100644 --- a/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java +++ b/Mage/src/main/java/mage/game/command/planes/TheEonFogPlane.java @@ -62,16 +62,14 @@ public class TheEonFogPlane extends Plane { class TheEonFogSkipUntapStepEffect extends ContinuousRuleModifyingEffectImpl { - boolean allPlayers = false; + final boolean allPlayers; public TheEonFogSkipUntapStepEffect() { - super(Duration.WhileOnBattlefield, Outcome.Neutral, false, false); - this.allPlayers = false; - staticText = "Players skip their untap steps"; + this(Duration.WhileOnBattlefield, false); } - public TheEonFogSkipUntapStepEffect(Duration d, boolean allPlayers) { - super(d, Outcome.Neutral, false, false); + public TheEonFogSkipUntapStepEffect(Duration duration, boolean allPlayers) { + super(duration, Outcome.Neutral, false, false); this.allPlayers = allPlayers; staticText = "Players skip their untap steps"; } @@ -86,6 +84,11 @@ class TheEonFogSkipUntapStepEffect extends ContinuousRuleModifyingEffectImpl { return new TheEonFogSkipUntapStepEffect(this); } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UNTAP_STEP; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { Plane cPlane = game.getState().getCurrentPlane(); @@ -95,6 +98,6 @@ class TheEonFogSkipUntapStepEffect extends ContinuousRuleModifyingEffectImpl { if (!cPlane.getPlaneType().equals(Planes.PLANE_THE_EON_FOG)) { return false; } - return event.getType() == GameEvent.EventType.UNTAP_STEP; + return true; } } diff --git a/Mage/src/main/java/mage/game/events/TableEvent.java b/Mage/src/main/java/mage/game/events/TableEvent.java index 44d21da6b63..ef9af06af82 100644 --- a/Mage/src/main/java/mage/game/events/TableEvent.java +++ b/Mage/src/main/java/mage/game/events/TableEvent.java @@ -7,6 +7,7 @@ import mage.game.draft.Draft; import mage.game.match.MatchOptions; import mage.game.tournament.MultiplayerRound; import mage.game.tournament.TournamentPairing; +import org.apache.log4j.Logger; import java.io.Serializable; import java.util.EventObject; @@ -17,11 +18,15 @@ import java.util.UUID; */ public class TableEvent extends EventObject implements ExternalEvent, Serializable { + private static final Logger logger = Logger.getLogger(TableEvent.class); + public enum EventType { UPDATE, INFO, STATUS, START_DRAFT, START_MATCH, SIDEBOARD, CONSTRUCT, SUBMIT_DECK, END, END_GAME_INFO, ERROR, INIT_TIMER, RESUME_TIMER, PAUSE_TIMER, CHECK_STATE_PLAYERS, START_MULTIPLAYER_MATCH } + + private Game game; private Draft draft; private EventType eventType; diff --git a/Mage/src/main/java/mage/game/match/MatchOptions.java b/Mage/src/main/java/mage/game/match/MatchOptions.java index dc0dfb0eee6..16047fc41a3 100644 --- a/Mage/src/main/java/mage/game/match/MatchOptions.java +++ b/Mage/src/main/java/mage/game/match/MatchOptions.java @@ -1,6 +1,7 @@ package mage.game.match; +import mage.cards.decks.DeckCardInfo; import mage.constants.MatchBufferTime; import mage.constants.MatchTimeLimit; import mage.constants.MultiplayerAttackOption; @@ -11,10 +12,7 @@ import mage.game.result.ResultProtos; import mage.players.PlayerType; import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @@ -52,6 +50,9 @@ public class MatchOptions implements Serializable { protected MatchBufferTime matchBufferTime; // Amount of time each player gets before their normal time limit counts down. Refreshes each time the normal timer is invoked. protected MulliganType mulliganType; + protected Collection perPlayerEmblemCards; + protected Collection globalEmblemCards; + /*public MatchOptions(String name, String gameType) { this.name = name; this.gameType = gameType; @@ -65,6 +66,8 @@ public class MatchOptions implements Serializable { this.password = ""; this.multiPlayer = multiPlayer; this.numSeats = numSeats; + this.perPlayerEmblemCards = Collections.emptySet(); + this.globalEmblemCards = Collections.emptySet(); } public void setNumSeats (int numSeats) { @@ -288,4 +291,19 @@ public class MatchOptions implements Serializable { return mulliganType; } + public Collection getPerPlayerEmblemCards() { + return perPlayerEmblemCards; + } + + public void setPerPlayerEmblemCards(Collection perPlayerEmblemCards) { + this.perPlayerEmblemCards = perPlayerEmblemCards; + } + + public Collection getGlobalEmblemCards() { + return globalEmblemCards; + } + + public void setGlobalEmblemCards(Collection globalEmblemCards) { + this.globalEmblemCards = globalEmblemCards; + } } diff --git a/Mage/src/main/java/mage/game/permanent/token/JaceCunningCastawayIllusionToken.java b/Mage/src/main/java/mage/game/permanent/token/JaceCunningCastawayIllusionToken.java index af042eb79d6..034e280d2d2 100644 --- a/Mage/src/main/java/mage/game/permanent/token/JaceCunningCastawayIllusionToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/JaceCunningCastawayIllusionToken.java @@ -1,16 +1,11 @@ - package mage.game.permanent.token; +import mage.MageInt; +import mage.abilities.common.BecomesTargetSourceTriggeredAbility; +import mage.abilities.effects.common.SacrificeSourceEffect; import mage.constants.CardType; import mage.constants.SubType; -import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.common.SacrificeSourceEffect; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.Spell; +import mage.filter.StaticFilters; /** * @author TheElk801 @@ -26,10 +21,11 @@ public final class JaceCunningCastawayIllusionToken extends TokenImpl { power = new MageInt(2); toughness = new MageInt(2); - this.addAbility(new IllusionTokenTriggeredAbility()); + this.addAbility(new BecomesTargetSourceTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it"), StaticFilters.FILTER_SPELL_A) + .setTriggerPhrase("When this creature becomes the target of a spell, ")); } - protected JaceCunningCastawayIllusionToken(final JaceCunningCastawayIllusionToken token) { + private JaceCunningCastawayIllusionToken(final JaceCunningCastawayIllusionToken token) { super(token); } @@ -38,39 +34,3 @@ public final class JaceCunningCastawayIllusionToken extends TokenImpl { return new JaceCunningCastawayIllusionToken(this); } } - -class IllusionTokenTriggeredAbility extends TriggeredAbilityImpl { - - public IllusionTokenTriggeredAbility() { - super(Zone.BATTLEFIELD, new SacrificeSourceEffect(), false); - } - - protected IllusionTokenTriggeredAbility(final IllusionTokenTriggeredAbility ability) { - super(ability); - } - - @Override - public IllusionTokenTriggeredAbility copy() { - return new IllusionTokenTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - MageObject eventSourceObject = game.getObject(event.getSourceId()); - if (event.getTargetId().equals(this.getSourceId()) && eventSourceObject instanceof Spell) { - return true; - } - return false; - } - - @Override - public String getRule() { - return "When this creature becomes the target of a spell, sacrifice it."; - } - -} diff --git a/Mage/src/main/java/mage/players/net/UserData.java b/Mage/src/main/java/mage/players/net/UserData.java index 679840526d4..1b99b4d2c59 100644 --- a/Mage/src/main/java/mage/players/net/UserData.java +++ b/Mage/src/main/java/mage/players/net/UserData.java @@ -22,7 +22,7 @@ public class UserData implements Serializable { protected boolean manaPoolAutomaticRestricted; protected boolean passPriorityCast; protected boolean passPriorityActivation; - protected boolean autoOrderTrigger; + protected boolean autoOrderTrigger; // auto-order triggers with same rule text protected int autoTargetLevel; protected boolean useSameSettingsForReplacementEffects; protected boolean useFirstManaAbility = false; diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 18ecb0892f0..c5b827b9c3b 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -38,6 +38,7 @@ import mage.game.permanent.PermanentMeld; import mage.game.permanent.PermanentToken; import mage.game.permanent.token.Token; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.target.TargetCard; @@ -1009,6 +1010,63 @@ public final class CardUtil { .collect(Collectors.toSet()); } + /** + * For finding the spell or ability on the stack for "becomes the target" triggers. + * @param event the GameEvent.EventType.TARGETED from checkTrigger() or watch() + * @param game the Game from checkTrigger() or watch() + * @return the StackObject which targeted the source, or null if not found + */ + public static StackObject getTargetingStackObject(GameEvent event, Game game) { + // In case of multiple simultaneous triggered abilities from the same source, + // need to get the actual one that targeted, see #8026, #8378 + // Also avoids triggering on cancelled selections, see #8802 + for (StackObject stackObject : game.getStack()) { + Ability stackAbility = stackObject.getStackAbility(); + if (stackAbility == null || !stackAbility.getSourceId().equals(event.getSourceId())) { + continue; + } + for (Target target : stackAbility.getTargets()) { + if (target.getTargets().contains(event.getTargetId())) { + return stackObject; + } + } + } + return null; + } + + /** + * For ensuring that spells/abilities that target the same object twice only trigger each "becomes the target" ability once. + * If this is the first attempt at triggering for a given ability targeting a given object, + * this method records that in the game state for later checks by this same method. + * @param checkingReference must be unique for each usage (this.id.toString() of the TriggeredAbility, or this.getKey() of the watcher) + * @param targetingObject from getTargetingStackObject + * @param event the GameEvent.EventType.TARGETED from checkTrigger() or watch() + * @param game the Game from checkTrigger() or watch() + * @return true if already triggered/watched, false if this is the first/only trigger/watch + */ + public static boolean checkTargetedEventAlreadyUsed(String checkingReference, StackObject targetingObject, GameEvent event, Game game) { + String stateKey = "targetedMap" + checkingReference; + // 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. (2021-02-05) + Map> targetMap = (Map>) game.getState().getValue(stateKey); + // targetMap: key - targetId; value - Set of stackObject Ids + if (targetMap == null) { + targetMap = new HashMap<>(); + } else { + targetMap = new HashMap<>(targetMap); // must have new object reference if saved back to game state + } + Set targetingObjects = targetMap.computeIfAbsent(event.getTargetId(), k -> new HashSet<>()); + if (!targetingObjects.add(targetingObject.getId())) { + return true; // The trigger/watcher already recorded that target of the stack object + } + // Otherwise, store this combination of trigger/watcher + target + stack object + targetMap.put(event.getTargetId(), targetingObjects); + game.getState().setValue(stateKey, targetMap); + return false; + } + /** * Put card to battlefield without resolve (for cheats and tests only) * diff --git a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java index 1df8fa84d1f..c97cf9436f3 100644 --- a/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/NumberOfTimesPermanentTargetedATurnWatcher.java @@ -1,16 +1,17 @@ - - package mage.watchers.common; -import java.util.HashMap; -import java.util.Map; import mage.MageObjectReference; import mage.constants.WatcherScope; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.util.CardUtil; import mage.watchers.Watcher; +import java.util.HashMap; +import java.util.Map; + /** * * @author LevelX2 @@ -25,24 +26,23 @@ public class NumberOfTimesPermanentTargetedATurnWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.TARGETED) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null) { - MageObjectReference mor = new MageObjectReference(permanent, game); - int amount = 0; - if (permanentsTargeted.containsKey(mor)) { - amount = permanentsTargeted.get(mor); - } - permanentsTargeted.put(mor, ++amount); - } + if (event.getType() != GameEvent.EventType.TARGETED) { + return; + } + StackObject targetingObject = CardUtil.getTargetingStackObject(event, game); + if (targetingObject == null || CardUtil.checkTargetedEventAlreadyUsed(this.getKey(), targetingObject, event, game)) { + return; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null) { + MageObjectReference mor = new MageObjectReference(permanent, game); + int nTimes = permanentsTargeted.getOrDefault(mor, 0); + permanentsTargeted.put(mor, nTimes + 1); } } - public boolean notMoreThanOnceTargetedThisTurn(Permanent creature, Game game) { - if (permanentsTargeted.containsKey(new MageObjectReference(creature, game))) { - return permanentsTargeted.get(new MageObjectReference(creature, game)) < 2; - } - return true; + public int numTimesTargetedThisTurn(Permanent permanent, Game game) { + return permanentsTargeted.getOrDefault(new MageObjectReference(permanent, game), 0); } @Override diff --git a/Utils/known-sets.txt b/Utils/known-sets.txt index 86925e7a233..fe412893618 100644 --- a/Utils/known-sets.txt +++ b/Utils/known-sets.txt @@ -132,6 +132,7 @@ Judgment|Judgment| Jumpstart|Jumpstart| Jumpstart: Historic Horizons|JumpstartHistoricHorizons| Jumpstart 2022|Jumpstart2022| +Jurassic World Collection|JurassicWorldCollection| Kaladesh|Kaladesh| Kaldheim|Kaldheim| Kaldheim Commander|KaldheimCommander| @@ -146,6 +147,8 @@ Limited Edition Beta|LimitedEditionBeta| The Lord of the Rings: Tales of Middle-earth|TheLordOfTheRingsTalesOfMiddleEarth| Tales of Middle-earth Commander|TalesOfMiddleEarthCommander| Lorwyn|Lorwyn| +Lost Caverns of Ixalan|LostCavernsOfIxalan| +Lost Caverns of Ixalan Commander|LostCavernsOfIxalanCommander| Magic 2010|Magic2010| Magic 2011|Magic2011| Magic 2012|Magic2012| @@ -212,6 +215,7 @@ Seventh Edition|SeventhEdition| Shadowmoor|Shadowmoor| Shadows over Innistrad|ShadowsOverInnistrad| Shards of Alara|ShardsOfAlara| +Special Guests|SpecialGuests| Starter 1999|Starter1999| Starter 2000|Starter2000| Star Wars|StarWars| diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 09d9d1aa519..05b4c8a0a21 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -12101,6 +12101,22 @@ Whirlpool Whelm|Lorwyn|96|C|{1}{U}|Instant|||Clash with an opponent, then return Wings of Velis Vel|Lorwyn|97|C|{1}{U}|Tribal Instant - Shapeshifter|||Changeling (This card is every creature type at all times.)$Target creature becomes 4/4, gains all creature types, and gains flying until end of turn.| Zephyr Net|Lorwyn|98|C|{1}{U}|Enchantment - Aura|||Enchant creature$Enchanted creature has defender and flying.| Black Poplar Shaman|Lorwyn|99|C|{2}{B}|Creature - Treefolk Shaman|1|3|{2}{B}: Regenerate target Treefolk.| +Breeches, Eager Pillager|Lost Caverns of Ixalan|137|R|{2}{R}|Legendary Creature - Goblin Pirate|3|3|First strike$Whenever a Pirate you control attacks, choose one that hasn't been chosen this turn --$* Create a Treasure token.$* Target creature can't block this turn.$* Exile the top card of your library. You may play it this turn.| +Ojer Axonil, Deepest Might|Lost Caverns of Ixalan|158|M|{2}{R}{R}|Legendary Creature - God|4|4|Trample$If a red source you control would deal an amount of noncombat damage less than Ojer Axonil's power to an opponent, that source deals damage equal to Ojer Axonil's power instead.$When Ojer Axonil dies, return it to the battlefield tapped and transformed under its owner's control.| +Temple of Power|Lost Caverns of Ixalan|158|M||Land|||(Transforms from Ojer Axonil, Deepest Might.)${T}: Add {R}.${2}{R}, {T}: Transform Temple of Power. Activate only if red sources you controlled dealt 4 or more noncombat damage this turn and only as a sorcery.| +Ghalta, Stampede Tyrant|Lost Caverns of Ixalan|185|M|{5}{G}{G}{G}|Legendary Creature - Elder Dinosaur|12|12|Trample$When Ghalta, Stampede Tyrant enters the battlefield, put any number of creature cards from your hand onto the battlefield.| +Huatli, Poet of Unity|Lost Caverns of Ixalan|189|M|{2}{G}|Legendary Creature - Human Warrior Bard|2|3|When Huatli, Poet of Unity enters the battlefield, search your library for a basic land card, reveal it, put it into your hand, then shuffle.${3}{R/W}{R/W}: Exile Huatli, then return her to the battlefield transformed under her owner's control. Activate only as a sorcery.| +Roar of the Fifth People|Lost Caverns of Ixalan|189|M||Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I -- Create two 3/3 green Dinosaur creature tokens.$II -- Roar of the Fifth People gains "Creatures you control have '{T}: Add {R}, {G}, or {W}.'"$III -- Search your library for a Dinosaur card, reveal it, put it into your hand, then shuffle.$IV -- Dinosaurs you control gain double strike and trample until end of turn.| +The Skullspore Nexus|Lost Caverns of Ixalan|212|M|{6}{G}{G}|Legendary Artifact|||This spell costs {X} less to cast, where X is the greatest power among creatures you control.$Whenever one or more nontoken creatures you control die, create a green Fungus Dinosaur creature token with base power and toughness each equal to the total power of those creatures.${2}, {T}: Double target creature's power until end of turn.| +Kellan, Daring Traveler|Lost Caverns of Ixalan|231|R|{1}{W}|Legendary Creature - Human Faerie Scout|2|3|Whenever Kellan, Daring Traveler attacks, reveal the top card of your library. If it's a creature card with mana value 3 or less, put it into your hand. Otherwise, you may put it into your graveyard.| +Journey On|Lost Caverns of Ixalan|231|R|{G}|Sorcery - Adventure|2|3|Create X Map tokens, where X is one plus the number of opponents who control an artifact.| +Cavern of Souls|Lost Caverns of Ixalan|269|M||Land|||As Cavern of Souls enters the battlefield, choose a creature type.${T}: Add {C}.${T}: Add one mana of any color. Spend this mana only to cast a creature spell of the chosen type, and that spell can't be countered.| +Plains|Lost Caverns of Ixalan|287|C||Basic Land - Plains|||({T}: Add {W}.)| +Island|Lost Caverns of Ixalan|288|C||Basic Land - Island|||({T}: Add {U}.)| +Swamp|Lost Caverns of Ixalan|289|C||Basic Land - Swamp|||({T}: Add {B}.)| +Mountain|Lost Caverns of Ixalan|290|C||Basic Land - Mountain|||({T}: Add {R}.)| +Forest|Lost Caverns of Ixalan|291|C||Basic Land - Forest|||({T}: Add {G}.)| +Coercive Portal|Lost Caverns of Ixalan Commander|109|M|{4}|Artifact|||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.| Ajani Goldmane|Magic 2010|1|M|{2}{W}{W}|Legendary Planeswalker - Ajani|||+1: You gain 2 life.$-1: Put a +1/+1 counter on each creature you control. Those creatures gain vigilance until end of turn.$-6: Put a white Avatar creature token onto the battlefield. It has "This creature's power and toughness are each equal to your life total."| Excommunicate|Magic 2010|10|C|{2}{W}|Sorcery|||Put target creature on top of its owner's library.| Hypnotic Specter|Magic 2010|100|R|{1}{B}{B}|Creature - Specter|2|2|Flying$Whenever Hypnotic Specter deals damage to an opponent, that player discards a card at random.| @@ -22927,6 +22943,8 @@ Crucible of Fire|Shards of Alara|96|R|{3}{R}|Enchantment|||Dragon creatures you Dragon Fodder|Shards of Alara|97|C|{1}{R}|Sorcery|||Put two 1/1 red Goblin creature tokens onto the battlefield.| Dragon's Herald|Shards of Alara|98|U|{R}|Creature - Goblin Shaman|1|1|{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.| Exuberant Firestoker|Shards of Alara|99|U|{2}{R}|Creature - Human Druid Shaman|1|1|At the beginning of your end step, if you control a creature with power 5 or greater, you may have Exuberant Firestoker deal 2 damage to target player.${tap}: Add {C}.| +Lord of Atlantis|Special Guests|1|R|{U}{U}|Creature - Merfolk|2|2|Other Merfolk get +1/+1 and have islandwalk. (They can't be blocked as long as defending player controls an Island.)| +Mana Crypt|Special Guests|17|M|{0}|Artifact|||At the beginning of your upkeep, flip a coin. If you lose the flip, Mana Crypt deals 3 damage to you.${T}: Add {C}{C}.| Angel of Light|Starter 1999|1|U|{4}{W}|Creature - Angel|3|3|Flying, vigilance| Breath of Life|Starter 1999|10|U|{3}{W}|Sorcery|||Return target creature card from your graveyard to the battlefield.| Goblin Commando|Starter 1999|100|U|{4}{R}|Creature - Goblin|2|2|When Goblin Commando enters the battlefield, it deals 2 damage to target creature.| @@ -29684,6 +29702,10 @@ Thirsting Axe|Eldritch Moon|202|U|{3}|Artifact - Equipment|||Equipped creature g Geier Reach Sanitarium|Eldritch Moon|203|R||Legendary Land|||{T}: Add {C}.${2}, {T}: Each player draws a card, then discards a card.| Hanweir Battlements|Eldritch Moon|204|R||Land|||{T}: Add {C}.${R},{T}: Target creature gains haste until end of turn.${3}{R}{R},{T}: If you both own and control Hanweir Battlements and a creature named Hanweir Garrison, exile them, then meld them into Hanweir, the Writhing Township.| Nephalia Academy|Eldritch Moon|205|U||Land|||If a spell or ability an opponent controls causes you to discard a card, you may reveal that card and put it on top of your library instead of putting it anywhere else.${T}: Add {C}.| +Welcome to...|Jurassic World Collection|7|R|{1}{G}{G}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter.)$I -- For each opponent, up to one target noncreature artifact they control becomes a 0/4 Wall artifact creature with defender for as long as you control this Saga.$II -- Create a 3/3 green Dinosaur creature token with trample. It gains haste until end of turn.$III -- Destroy all Walls. Exile this Saga, then return it to the battlefield transformed under your control.| +Jurassic Park|Jurassic World Collection|7|R||Legendary Land|||(Transforms from Welcome to ....)$Each Dinosaur card in your graveyard has escape. The escape cost is equal to the card's mana cost plus exile three other cards from your graveyard. (You may cast cards from your graveyard for their escape cost.)${T}: Add {G} for each Dinosaur you control.| +Ian Malcolm, Chaotician|Jurassic World Collection|13|R|{1}{U}{R}|Legendary Creature - Human Scientist|2|2|Whenever a player draws their second card each turn, that player exiles the top card of their library.$During each player's turn, that player may cast a spell from among the cards they don't own exiled with Ian Malcolm, Chaotician, and mana of any type can be spent to cast it.| +Indominus Rex, Alpha|Jurassic World Collection|14|R|{1}{U/B}{U/B}{G}{G}|Legendary Creature - Dinosaur Mutant|6|6|As Indominus Rex, Alpha enters the battlefield, discard any number of creature cards. It enters with a flying counter on it if a card discarded this way has flying. The same is true for first strike, double strike, deathtouch, hexproof, haste, indestructible, lifelink, menace, reach, trample, and vigilance.$When Indominus Rex enters the battlefield, draw a card for each counter on it.| Acrobatic Maneuver|Kaladesh|1|C|{2}{W}|Instant|||Exile target creature you control, then return that card to the battlefield under its owner's control.$Draw a card. | Aerial Responder|Kaladesh|2|U|{1}{W}{W}|Creature - Dwarf Soldier|2|3|Flying, vigilance, lifelink| Aetherstorm Roc|Kaladesh|3|R|{2}{W}{W}|Creature - Bird|3|3|Flying$Whenever Aetherstorm Roc or another creature enters the battlefield under your control, you get {E} (an energy counter).$Whenever Aetherstorm Roc attacks, you may pay {E}{E}. If you do, put a +1/+1 creature on it and tap up to one target creature defending player controls.| @@ -49101,86 +49123,63 @@ Galadriel, Gift-Giver|The Lord of the Rings: Tales of Middle-earth|296|R|{3}{G}{ The Balrog, Flame of Udun|The Lord of the Rings: Tales of Middle-earth|297|R|{3}{B}{R}|Legendary Creature - Avatar Demon|7|7|Trample$When a legendary creature an opponent controls dies, put The Balrog, Flame of Udun on the bottom of its owner's library.| Bilbo's Ring|The Lord of the Rings: Tales of Middle-earth|298|R|{3}|Legendary Artifact - Equipment|||As long as it's your turn, equipped creature has hexproof and can't be blocked.$Whenever equipped creature attacks alone, you draw a card and you lose 1 life.$Equip Halfling {1}$Equip {4}| Trailblazer's Boots|The Lord of the Rings: Tales of Middle-earth|398|R|{2}|Artifact - Equipment|||Equipped creature has nonbasic landwalk.$Equip {2}| -Eowyn, Shieldmaiden|Tales of Middle-earth Commander|1|M|{2}{U}{R}{W}|Legendary Creature - Human Knight|5|4|First strike$At the beginning of combat on your turn, if another Human entered the battlefield under your control this turn, create two 2/2 red Human Knight creature tokens with trample and haste. Then if you control six or more Humans, draw a card.| +Eagle of Deliverance|The Lord of the Rings: Tales of Middle-earth|824|R|{4}{W}{W}|Creature - Bird Soldier|5|5|Flying$When Eagle of Deliverance enters the battlefield, put an indestructible counter on another target creature you control. Draw a card if that creature's power is 2 or less.| +Minas Tirith Garrison|The Lord of the Rings: Tales of Middle-earth|825|R|{3}{U}|Creature - Human Soldier|*|5|Minas Tirith Garrison's power is equal to the number of cards in your hand.$Whenever Minas Tirith Garrison attacks, you may tap any number of untapped Humans you control. Draw a card for each Human tapped this way.| +Warg Rider|The Lord of the Rings: Tales of Middle-earth|826|R|{4}{B}|Creature - Orc Warrior|4|3|Menace$Other Orcs and Goblins you control have menace.$At the beginning of combat on your turn, amass Orcs 2. (Put two +1/+1 counters on an Army you control. It's also an Orc. If you don't control an Army, create a 0/0 black Orc Army creature token first.)| +Riders of the Mark|The Lord of the Rings: Tales of Middle-earth|827|R|{6}{R}|Creature - Human Knight|7|4|This spell costs {1} less to cast for each Human you control.$Trample, haste$At the beginning of your end step, if Riders of the Mark attacked this turn, return it to its owner's hand. If you do, create a number of 1/1 white Human Soldier creature tokens equal to its toughness.| +Mirkwood Channeler|The Lord of the Rings: Tales of Middle-earth|828|R|{3}{G}|Creature - Elf Druid|3|3|At the beginning of combat on your turn, target Elf you control gains trample and gets +X/+X until end of turn, where X is the number of Forests you control.| Frodo, Adventurous Hobbit|Tales of Middle-earth Commander|2|M|{W}{B}|Legendary Creature - Halfling Scout|1|3|Partner with Sam, Loyal Attendant$Vigilance$Whenever Frodo, Adventurous Hobbit attacks, if you gained 3 or more life this turn, the Ring tempts you. Then if Frodo is your Ring-bearer and the Ring has tempted you two or more times this game, draw a card.| -Galadriel, Elven-Queen|Tales of Middle-earth Commander|3|M|{2}{G}{U}|Legendary Creature - Elf Noble|4|5|Will of the council -- At the beginning of combat on your turn, if another Elf entered the battlefield under your control this turn, starting with you, each player votes for dominion or guidance. If dominion gets more votes, the Ring tempts you, then you put a +1/+1 counter on your Ring-bearer. If guidance gets more votes or the vote is tied, draw a card.| -Sauron, Lord of the Rings|Tales of Middle-earth Commander|4|M|{5}{U}{B}{R}|Legendary Creature - Avatar Horror|9|9|When you cast this spell, amass Orcs 5, mill five cards, then return a creature card from your graveyard to the battlefield.$Trample$Whenever a commander an opponent controls dies, the Ring tempts you.| -Aragorn, King of Gondor|Tales of Middle-earth Commander|5|M|{1}{U}{R}{W}|Legendary Creature - Human Noble|4|4|Vigilance, lifelink$When Aragorn, King of Gondor enters the battlefield, you become the monarch.$Whenever Aragorn attacks, up to one target creature can't block this turn. If you're the monarch, creatures can't block this turn.| -Gandalf, Westward Voyager|Tales of Middle-earth Commander|6|M|{3}{G}{U}|Legendary Creature - Avatar Wizard|5|5|Whenever you cast a spell with mana value 5 or greater, each opponent reveals the top card of their library. If any of those cards shares a card type with that spell, copy that spell, you may choose new targets for the copy, and each opponent draws a card. Otherwise, you draw a card.| -Sam, Loyal Attendant|Tales of Middle-earth Commander|7|M|{1}{G}{W}|Legendary Creature - Halfling Peasant|2|4|Partner with Frodo, Adventurous Hobbit$At the beginning of combat on your turn, create a Food token.$Activated abilities of Foods you control cost {1} less to activate.| Saruman, the White Hand|Tales of Middle-earth Commander|8|M|{1}{U}{B}{R}|Legendary Creature - Avatar Wizard|2|5|Whenever you cast a noncreature spell, amass Orcs X, where X is that spell's mana value.$Goblins and Orcs you control have ward {2}.| Beregond of the Guard|Tales of Middle-earth Commander|9|R|{3}{W}|Legendary Creature - Human Soldier|3|3|Whenever Beregond of the Guard or another Human enters the battlefield under your control, creatures you control get +1/+1 and gain vigilance until end of turn.| Champions of Minas Tirith|Tales of Middle-earth Commander|10|R|{5}{W}|Creature - Human Soldier|4|6|When Champions of Minas Tirith enters the battlefield, you become the monarch.$At the beginning of combat on each opponent's turn, if you're the monarch, that opponent may pay {X}, where X is the number of cards in their hand. If they don't, they can't attack you this combat.| Field-Tested Frying Pan|Tales of Middle-earth Commander|11|R|{2}{W}|Artifact - Equipment|||When Field-Tested Frying Pan enters the battlefield, create a Food token, then create a 1/1 white Halfling creature token and attach Field-Tested Frying Pan to it.$Equipped creature has "Whenever you gain life, this creature gets +X/+X until end of turn, where X is the amount of life you gained."$Equip {2}| -The Gaffer|Tales of Middle-earth Commander|12|R|{2}{W}|Legendary Creature - Halfling Peasant|2|3|At the beginning of each end step, if you gained 3 or more life this turn, draw a card.| -Gilraen, Dunedain Protector|Tales of Middle-earth Commander|13|R|{2}{W}|Legendary Creature - Human Noble|2|3|{2}, {T}: Exile another target creature you control. You may return that card to the battlefield under its owner's control. If you don't, at the beginning of the next end step, return that card to the battlefield under its owner's control with a vigilance counter and a lifelink counter on it.| -Grey Host Reinforcements|Tales of Middle-earth Commander|14|R|{3}{W}|Creature - Spirit Soldier|1|1|Flying, ward {3}$When Grey Host Reinforcements enters the battlefield, exile target player's graveyard. Put a number of +1/+1 counters on Grey Host Reinforcements equal to the number of creature cards exiled this way.| Gwaihir, Greatest of the Eagles|Tales of Middle-earth Commander|15|R|{4}{W}|Legendary Creature - Bird Noble|5|5|Flying$Whenever Gwaihir attacks, target attacking creature gains flying until end of turn.$At the beginning of each end step, if you gained 3 or more life this turn, create a 3/3 white Bird creature token with flying and "Whenever this creature attacks, target attacking creature gains flying until end of turn."| Lossarnach Captain|Tales of Middle-earth Commander|16|R|{3}{W}|Creature - Human Soldier|3|1|First strike$Whenever Lossarnach Captain or another Human enters the battlefield under your control, tap target creature an opponent controls.$At the beginning of your upkeep, create a 1/1 white Human Soldier creature token.| Of Herbs and Stewed Rabbit|Tales of Middle-earth Commander|17|R|{2}{W}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Put a +1/+1 counter on up to one target creature. Create a Food token.$II -- Draw a card. Create a Food token.$III -- Create a 1/1 white Halfling creature token for each Food you control.| -Archivist of Gondor|Tales of Middle-earth Commander|18|R|{2}{U}|Creature - Human Advisor|2|3|When your commander deals combat damage to a player, if there is no monarch, you become the monarch.$At the beginning of the monarch's end step, that player draws a card.| Corsairs of Umbar|Tales of Middle-earth Commander|19|R|{3}{U}|Creature - Human Pirate|3|3|{2}{U}: Target Goblin, Orc, or Pirate can't be blocked this turn.$Whenever Corsairs of Umbar deals combat damage to a player, amass Orcs 3.| Denethor, Stone Seer|Tales of Middle-earth Commander|20|R|{1}{U}|Legendary Creature - Human Noble|1|3|When Denethor, Stone Seer enters the battlefield, scry 2.${3}{R}, {T}, Sacrifice Denethor: Target player becomes the monarch. Denethor deals 3 damage to any target.| Fealty to the Realm|Tales of Middle-earth Commander|21|R|{4}{U}|Enchantment - Aura|||Enchant creature$When Fealty to the Realm enters the battlefield, you become the monarch.$The monarch controls enchanted creature.$Enchanted creature attacks each combat if able and can't attack you.| -Monstrosity of the Lake|Tales of Middle-earth Commander|22|R|{4}{U}|Legendary Creature - Kraken|4|6|When Monstrosity of the Lake enters the battlefield, you may pay {5}. If you do, tap all creatures your opponents control, then put a stun counter on each of those creatures.$Islandcycling {2}| Raise the Palisade|Tales of Middle-earth Commander|23|R|{4}{U}|Sorcery|||Choose a creature type. Return all creatures that aren't of the chosen type to their owners' hands.| -Subjugate the Hobbits|Tales of Middle-earth Commander|24|R|{5}{U}{U}|Sorcery|||Gain control of each noncommander creature with mana value 3 or less.| -Trap the Trespassers|Tales of Middle-earth Commander|25|R|{2}{U}|Instant|||Secret council -- Each player secretly votes for a creature you don't control, then those votes are revealed. For each creature with one or more votes, put that many stun counters on it, then tap it.| -Gollum, Obsessed Stalker|Tales of Middle-earth Commander|26|R|{1}{B}|Legendary Creature - Halfling Horror|1|1|Skulk$At the beginning of your end step, each opponent dealt combat damage this game by a creature named Gollum, Obsessed Stalker loses life equal to the amount of life you gained this turn.| Lobelia, Defender of Bag End|Tales of Middle-earth Commander|27|R|{2}{B}|Legendary Creature - Halfling Citizen|2|2|When Lobelia enters the battlefield, look at the top card of each opponent's library and exile those cards face down.${T}, Sacrifice an artifact: Choose one --$* Until end of turn, you may play a card exiled with Lobelia without paying its mana cost.$* Each opponent loses 2 life and you gain 2 life.| -Rapacious Guest|Tales of Middle-earth Commander|28|R|{2}{B}|Creature - Halfling Citizen|2|2|Menace$Whenever one or more creatures you control deal combat damage to a player, create a Food token.$Whenever you sacrifice a Food, put a +1/+1 counter on Rapacious Guest.$When Rapacious Guest leaves the battlefield, target opponent loses life equal to its power.| -Shelob, Dread Weaver|Tales of Middle-earth Commander|29|R|{3}{B}|Legendary Creature - Spider Demon|3|3|Whenever a nontoken creature an opponent controls dies, exile it.${2}{B}, Put a creature card exiled with Shelob, Dread Weaver into its owner's graveyard: Put two +1/+1 counters on Shelob. Draw a card.${X}{1}{B}: Put target creature card with mana value X exiled with Shelob onto the battlefield tapped under your control.| -Call for Aid|Tales of Middle-earth Commander|30|R|{4}{R}|Sorcery|||Gain control of all creatures target opponent controls until end of turn. Untap those creatures. They gain haste until end of turn. You can't attack that player this turn. You can't sacrifice those creatures this turn.| -Cavern-Hoard Dragon|Tales of Middle-earth Commander|31|R|{7}{R}{R}|Creature - Dragon|6|6|This spell costs {X} less to cast, where X is the greatest number of artifacts an opponent controls.$Flying, trample, haste$Whenever Cavern-Hoard Dragon deals combat damage to a player, you create a Treasure token for each artifact that player controls.| -Gimli of the Glittering Caves|Tales of Middle-earth Commander|32|R|{2}{R}|Legendary Creature - Dwarf Warrior|1|1|Double strike$Whenever another legendary creature enters the battlefield under your control, put a +1/+1 counter on Gimli of the Glittering Caves.$Whenever Gimli deals combat damage to a player, create a Treasure token.| -Orcish Siegemaster|Tales of Middle-earth Commander|33|R|{2}{R}|Creature - Orc Soldier|0|5|Trample$Other Orcs and Goblins you control have trample.$Whenever Orcish Siegemaster attacks, it gets +X/+0 until end of turn, where X is the greatest power among creatures you control.| -Rampaging War Mammoth|Tales of Middle-earth Commander|34|R|{5}{R}{R}|Creature - Elephant|9|7|Trample$Cycling {X}{2}{R}$When you cycle Rampaging War Mammoth, destroy up to X target artifacts.| -Arwen, Weaver of Hope|Tales of Middle-earth Commander|35|R|{1}{G}{G}|Legendary Creature - Elf Noble|2|1|Each other creature you control enters the battlefield with a number of additional +1/+1 counters on it equal to Arwen, Weaver of Hope's toughness.| -Assemble the Entmoot|Tales of Middle-earth Commander|36|R|{3}{G}|Enchantment|||Creatures you control have reach.$Sacrifice Assemble the Entmoot: Create three tapped X/X green Treefolk creature tokens, where X is the amount of life you gained this turn. Put a reach counter on each of them.| Feasting Hobbit|Tales of Middle-earth Commander|37|R|{1}{G}|Creature - Halfling Citizen|2|2|Devour Food 3$Creatures with power less than Feasting Hobbit's power can't block it.| Galadhrim Ambush|Tales of Middle-earth Commander|38|R|{3}{G}|Instant|||Create X 1/1 green Elf Warrior creature tokens, where X is the number of attacking creatures.$Prevent all combat damage that would be dealt this turn by non-Elf creatures.| Haldir, Lorien Lieutenant|Tales of Middle-earth Commander|39|R|{X}{G}|Legendary Creature - Elf Soldier|0|0|Haldir, Lorien Lieutenant enters the battlefield with X +1/+1 counters on it.$Vigilance${5}{G}: Until end of turn, other Elves you control gain vigilance and get +1/+1 for each +1/+1 counter on Haldir.| -Legolas Greenleaf|Tales of Middle-earth Commander|40|R|{2}{G}|Legendary Creature - Elf Archer|2|2|Reach$Legolas Greenleaf can't be blocked by creatures with power 2 or less.$Whenever another legendary creature enters the battlefield under your control, put a +1/+1 counter on Legolas Greenleaf.$Whenever Legolas Greenleaf deals combat damage to a player, draw a card.| -Mirkwood Elk|Tales of Middle-earth Commander|41|R|{5}{G}|Creature - Elk|6|6|Trample$Whenever Mirkwood Elk enters the battlefield or attacks, return target Elf card from your graveyard to your hand. You gain life equal to that card's power.| -Motivated Pony|Tales of Middle-earth Commander|42|R|{4}{G}|Creature - Horse|3|3|Trample, haste$Whenever Motivated Pony attacks, attacking creatures get +1/+1 until end of turn. If a Food entered the battlefield under your control this turn, untap those creatures and they get an additional +2/+2 until end of turn.| -Prize Pig|Tales of Middle-earth Commander|43|R|{1}{G}|Creature - Boar|0|3|Whenever you gain life, put that many ribbon counters on Prize Pig. Then if there are three or more ribbon counters on Prize Pig, remove those counters and untap it.${T}: Add one mana of any color.| -Travel Through Caradhras|Tales of Middle-earth Commander|44|R|{5}{G}|Sorcery|||Council's dilemma -- Starting with you, each player votes for Redhorn Pass or Mines of Moria. For each Redhorn Pass vote, search your library for a basic land card and put it onto the battlefield tapped. If you search your library this way, shuffle. For each Mines of Moria vote, return a card from your graveyard to your hand.$Exile Travel Through Caradhras.| -Windswift Slice|Tales of Middle-earth Commander|45|R|{2}{G}|Instant|||Target creature you control deals damage equal to its power to target creature you don't control. Create a number of 1/1 green Elf Warrior creature tokens equal to the amount of excess damage dealt this way.| The Balrog of Moria|Tales of Middle-earth Commander|46|R|{4}{B}{B}{R}|Legendary Creature - Avatar Demon|8|8|Trample, haste$When The Balrog of Moria dies, you may exile it. When you do, for each opponent, exile up to one target creature that player controls.$Cycling {3}{R}$When you cycle The Balrog of Moria, create two Treasure tokens.| Banquet Guests|Tales of Middle-earth Commander|47|R|{X}{G}{W}|Creature - Halfling Citizen|0|0|Affinity for Food$Trample$Banquet Guests enters the battlefield with twice X +1/+1 counters on it.${2}, Sacrifice a Food: Banquet Guests gains indestructible until end of turn.| -Bilbo, Birthday Celebrant|Tales of Middle-earth Commander|48|R|{W}{B}{G}|Legendary Creature - Halfling Rogue|2|3|If you would gain life, you gain that much life plus 1 instead.${2}{W}{B}{G}, {T}, Exile Bilbo, Birthday Celebrant: Search your library for any number of creature cards, put them onto the battlefield, then shuffle. Activate only if you have 111 or more life.| -Boromir, Gondor's Hope|Tales of Middle-earth Commander|49|R|{2}{W}{U}|Legendary Creature - Human Warrior|3|4|Whenever Boromir, Gondor's Hope enters the battlefield or attacks, look at the top six cards of your library. You may reveal a Human or artifact card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.| -Cirdan the Shipwright|Tales of Middle-earth Commander|50|R|{3}{G}{U}|Legendary Creature - Elf Noble|3|4|Vigilance$Secret council -- Whenever Cirdan the Shipwright enters the battlefield or attacks, each player secretly votes for a player, then those votes are revealed. Each player draws a card for each vote they received. Each player who received no votes may put a permanent card from their hand onto the battlefield.| -Elrond of the White Council|Tales of Middle-earth Commander|51|R|{3}{G}{U}|Legendary Creature - Elf Noble|3|3|Secret council -- When Elrond of the White Council enters the battlefield, each player secretly votes for fellowship or aid, then those votes are revealed. For each fellowship vote, the voter chooses a creature they control. You gain control of each creature chosen this way, and they gain "This creature can't attack its owner." Then for each aid vote, put a +1/+1 counter on each creature you control.| -Eomer, King of Rohan|Tales of Middle-earth Commander|52|R|{3}{R}{W}|Legendary Creature - Human Noble|2|2|Double strike$Eomer, King of Rohan enters the battlefield with a +1/+1 counter on it for each other Human you control.$When Eomer enters the battlefield, target player becomes the monarch. Eomer deals damage equal to its power to any target.| -Erestor of the Council|Tales of Middle-earth Commander|53|R|{1}{G}{U}|Legendary Creature - Elf Noble|2|4|Whenever players finish voting, each opponent who voted for a choice you voted for creates a Treasure token. You scry X, where X is the number of opponents who voted for a choice you didn't vote for. Draw a card.| -Faramir, Steward of Gondor|Tales of Middle-earth Commander|54|R|{1}{W}{U}|Legendary Creature - Human Noble|2|2|Whenever a legendary creature with mana value 4 or greater enters the battlefield under your control, you become the monarch.$At the beginning of your end step, if you're the monarch, create two 1/1 white Human Soldier creature tokens.| -Farmer Cotton|Tales of Middle-earth Commander|55|R|{X}{G}{W}|Legendary Creature - Halfling Peasant|1|1|When Farmer Cotton enters the battlefield, create X 1/1 white Halfling creature tokens and X Food tokens.| -Forth Eorlingas!|Tales of Middle-earth Commander|56|R|{X}{R}{W}|Sorcery|||Create X 2/2 red Human Knight creature tokens with trample and haste.$Whenever one or more creatures you control deal combat damage to one or more players this turn, you become the monarch.| -Grima, Saruman's Footman|Tales of Middle-earth Commander|57|R|{2}{U}{B}|Legendary Creature - Human Advisor|1|4|Grima, Saruman's Footman can't be blocked.$Whenever Grima 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.| -In the Darkness Bind Them|Tales of Middle-earth Commander|58|R|{2}{U}{B}{R}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I, II, III-- Create a 3/3 black Wraith creature token with menace. The Ring tempts you.$IV-- For each opponent, gain control of up to one target creature that player controls until end of turn. Untap those creatures. They gain haste until end of turn. The Ring tempts you.| +In the Darkness Bind Them|Tales of Middle-earth Commander|58|R|{2}{U}{B}{R}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I, II, III -- Create a 3/3 black Wraith creature token with menace. The Ring tempts you.$IV -- For each opponent, gain control of up to one target creature that player controls until end of turn. Untap those creatures. They gain haste until end of turn. The Ring tempts you.| Lidless Gaze|Tales of Middle-earth Commander|59|R|{2}{B}{R}|Sorcery|||Exile the top card of each player's library. Until the end of your next turn, you may play those cards, and mana of any type can be spent to cast them.$Flashback {2}{B}{R}| -Lord of the Nazgul|Tales of Middle-earth Commander|60|R|{3}{U}{B}|Legendary Creature - Wraith Noble|4|3|Flying$Wraiths you control have protection from Ring-bearers.$Whenever you cast an instant or sorcery spell, create a 3/3 black Wraith creature token with menace. Then if you control nine or more Wraiths, Wraiths you control have base power and toughness 9/9 until end of turn.| -Merry, Warden of Isengard|Tales of Middle-earth Commander|61|R|{1}{G}{W}|Legendary Creature - Halfling Advisor|1|4|Partner with Pippin, Warden of Isengard$Whenever one or more artifacts enter the battlefield under your control, create a 1/1 white Soldier creature token with lifelink. This ability triggers only once each turn.| Mirkwood Trapper|Tales of Middle-earth Commander|62|R|{1}{G}{U}|Creature - Elf Scout|1|4|Whenever a player attacks you, target attacking creature gets -2/-0 until end of turn.$Whenever a player attacks, if they aren't attacking you, that player chooses an attacking creature. It gets +2/+0 until end of turn.| Moria Scavenger|Tales of Middle-earth Commander|63|R|{1}{B}{R}|Creature - Orc Rogue|1|4|Deathtouch, haste${T}, Discard a card: Draw a card. If the discarded card was a creature card, amass Orcs 1.| -Oath of Eorl|Tales of Middle-earth Commander|64|R|{3}{R}{W}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I-- Create two 1/1 white Human Soldier creature tokens.$II-- Create two 2/2 red Human Knight creature tokens with trample and haste.$III-- Put an indestructible counter on up to one target Human. You become the monarch.| Pippin, Warden of Isengard|Tales of Middle-earth Commander|65|R|{B}{G}|Legendary Creature - Halfling Advisor|2|2|Partner with Merry, Warden of Isengard${1}, {T}: Create a Food token.${T}, Sacrifice four Foods: Other creatures you control get +3/+3 and gain haste until end of turn. Activate only as a sorcery.| Radagast, Wizard of Wilds|Tales of Middle-earth Commander|66|R|{2}{G}{U}|Legendary Creature - Avatar Wizard|3|5|Ward {1}$Beasts and Birds you control have ward {1}.$Whenever you cast a spell with mana value 5 or greater, choose one --$* Create a 3/3 green Beast creature token.$* Create a 2/2 blue Bird creature token with flying.| -Riders of Rohan|Tales of Middle-earth Commander|67|R|{3}{R}{W}|Creature - Human Knight|4|4|When Riders of Rohan enters the battlefield, create two 2/2 red Human Knight creature tokens with trample and haste.$Dash {4}{R}{W}| -Sail into the West|Tales of Middle-earth Commander|68|R|{2}{G}{U}|Instant|||Will of the council -- Starting with you, each player votes for return or embark. If return gets more votes, each player returns up to two cards from their graveyard to their hand, then you exile Sail into the West. If embark gets more votes or the vote is tied, each player may discard their hand and draw seven cards.| -Song of Earendil|Tales of Middle-earth Commander|69|R|{3}{G}{U}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I-- Scry 2, then draw two cards.$II-- Create a Treasure token and a 2/2 blue Bird creature token with flying.$III-- Put a flying counter on each creature you control without flying.| -Summons of Saruman|Tales of Middle-earth Commander|70|R|{X}{U}{R}|Sorcery|||Amass Orcs X. Mill X cards. You may cast an instant or sorcery spell with mana value X or less from among them without paying its mana cost.$Flashback--{3}{U}{R}, Exile X cards from your graveyard.| -Taunt from the Rampart|Tales of Middle-earth Commander|71|R|{3}{R}{W}|Sorcery|||Goad all creatures your opponents control. Until your next turn, those creatures can't block.| Too Greedily, Too Deep|Tales of Middle-earth Commander|72|R|{5}{B}{R}|Sorcery|||Put target creature card from a graveyard onto the battlefield under your control. That creature deals damage equal to its power to each other creature.| -Treebeard, Gracious Host|Tales of Middle-earth Commander|73|R|{2}{G}{W}|Legendary Creature - Treefolk|0|5|Trample, ward {2}$When Treebeard, Gracious Host enters the battlefield, create two Food tokens.$Whenever you gain life, put that many +1/+1 counters on target Halfling or Treefolk.| -Wake the Dragon|Tales of Middle-earth Commander|74|R|{4}{B}{R}|Sorcery|||Create a 6/6 black and red Dragon creature token with flying, menace, and "Whenever this creature deals combat damage to a player, gain control of target artifact that player controls."$Flashback {6}{B}{R}| Crown of Gondor|Tales of Middle-earth Commander|75|R|{3}|Legendary Artifact - Equipment|||Equipped creature gets +1/+1 for each creature you control.$When a legendary creature enters the battlefield under your control, if there is no monarch, you become the monarch.$Equip {4}. This ability costs {3} less to activate if you're the monarch.| -Hithlain Rope|Tales of Middle-earth Commander|76|R|{2}|Artifact|||Hithlain Rope can't be sacrificed.${1}, {T}: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle. The player to your right gains control of Hithlain Rope.${2}, {T}: Draw a card. The player to your right gains control of Hithlain Rope.| -Lothlorien Blade|Tales of Middle-earth Commander|77|R|{3}|Artifact - Equipment|||Whenever equipped creature attacks, it deals damage equal to its power to target creature defending player controls.$Equip Elf {2}$Equip {5}| -Model of Unity|Tales of Middle-earth Commander|78|R|{3}|Artifact|||Whenever players finish voting, you and each opponent who voted for a choice you voted for may scry 2.${T}: Add one mana of any color.| -Relic of Sauron|Tales of Middle-earth Commander|79|R|{4}|Artifact|||{T}: Add two mana in any combination of {U}, {B} and/or {R}.${3}, {T}: Draw two cards, then discard a card.| -The Black Gate|Tales of Middle-earth Commander|80|R||Legendary Land - Gate|||As The Black Gate enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped.${T}: Add {B}.${1}{B}, {T}: Choose a player with the most life or tied for most life. Target creature can't be blocked by creatures that player controls this turn.| +Eowyn, Shieldmaiden|Tales of Middle-earth Commander|81|M|{2}{U}{R}{W}|Legendary Creature - Human Knight|5|4|First strike$At the beginning of combat on your turn, if another Human entered the battlefield under your control this turn, create two 2/2 red Human Knight creature tokens with trample and haste. Then if you control six or more Humans, draw a card.| +Galadriel, Elven-Queen|Tales of Middle-earth Commander|83|M|{2}{G}{U}|Legendary Creature - Elf Noble|4|5|Will of the council -- At the beginning of combat on your turn, if another Elf entered the battlefield under your control this turn, starting with you, each player votes for dominion or guidance. If dominion gets more votes, the Ring tempts you, then you put a +1/+1 counter on your Ring-bearer. If guidance gets more votes or the vote is tied, draw a card.| +Gandalf, Westward Voyager|Tales of Middle-earth Commander|89|M|{3}{G}{U}|Legendary Creature - Avatar Wizard|5|5|Whenever you cast a spell with mana value 5 or greater, each opponent reveals the top card of their library. If any of those cards shares a card type with that spell, copy that spell, you may choose new targets for the copy, and each opponent draws a card. Otherwise, you draw a card.| +Sam, Loyal Attendant|Tales of Middle-earth Commander|90|M|{1}{G}{W}|Legendary Creature - Halfling Peasant|2|4|Partner with Frodo, Adventurous Hobbit$At the beginning of combat on your turn, create a Food token.$Activated abilities of Foods you control cost {1} less to activate.| +Gilraen, Dunedain Protector|Tales of Middle-earth Commander|97|R|{2}{W}|Legendary Creature - Human Noble|2|3|{2}, {T}: Exile another target creature you control. You may return that card to the battlefield under its owner's control. If you don't, at the beginning of the next end step, return that card to the battlefield under its owner's control with a vigilance counter and a lifelink counter on it.| +Subjugate the Hobbits|Tales of Middle-earth Commander|107|R|{5}{U}{U}|Sorcery|||Gain control of each noncommander creature with mana value 3 or less.| +Trap the Trespassers|Tales of Middle-earth Commander|108|R|{2}{U}|Instant|||Secret council -- Each player secretly votes for a creature you don't control, then those votes are revealed. For each creature with one or more votes, put that many stun counters on it, then tap it.| +Rapacious Guest|Tales of Middle-earth Commander|111|R|{2}{B}|Creature - Halfling Citizen|2|2|Menace$Whenever one or more creatures you control deal combat damage to a player, create a Food token.$Whenever you sacrifice a Food, put a +1/+1 counter on Rapacious Guest.$When Rapacious Guest leaves the battlefield, target opponent loses life equal to its power.| +Shelob, Dread Weaver|Tales of Middle-earth Commander|112|R|{3}{B}|Legendary Creature - Spider Demon|3|3|Whenever a nontoken creature an opponent controls dies, exile it.${2}{B}, Put a creature card exiled with Shelob, Dread Weaver into its owner's graveyard: Put two +1/+1 counters on Shelob. Draw a card.${X}{1}{B}: Put target creature card with mana value X exiled with Shelob onto the battlefield tapped under your control.| +Call for Aid|Tales of Middle-earth Commander|113|R|{4}{R}|Sorcery|||Gain control of all creatures target opponent controls until end of turn. Untap those creatures. They gain haste until end of turn. You can't attack that player this turn. You can't sacrifice those creatures this turn.| +Cavern-Hoard Dragon|Tales of Middle-earth Commander|114|R|{7}{R}{R}|Creature - Dragon|6|6|This spell costs {X} less to cast, where X is the greatest number of artifacts an opponent controls.$Flying, trample, haste$Whenever Cavern-Hoard Dragon deals combat damage to a player, you create a Treasure token for each artifact that player controls.| +Gimli of the Glittering Caves|Tales of Middle-earth Commander|115|R|{2}{R}|Legendary Creature - Dwarf Warrior|1|1|Double strike$Whenever another legendary creature enters the battlefield under your control, put a +1/+1 counter on Gimli of the Glittering Caves.$Whenever Gimli deals combat damage to a player, create a Treasure token.| +Orcish Siegemaster|Tales of Middle-earth Commander|116|R|{2}{R}|Creature - Orc Soldier|0|5|Trample$Other Orcs and Goblins you control have trample.$Whenever Orcish Siegemaster attacks, it gets +X/+0 until end of turn, where X is the greatest power among creatures you control.| +Assemble the Entmoot|Tales of Middle-earth Commander|119|R|{3}{G}|Enchantment|||Creatures you control have reach.$Sacrifice Assemble the Entmoot: Create three tapped X/X green Treefolk creature tokens, where X is the amount of life you gained this turn. Put a reach counter on each of them.| +Prize Pig|Tales of Middle-earth Commander|126|R|{1}{G}|Creature - Boar|0|3|Whenever you gain life, put that many ribbon counters on Prize Pig. Then if there are three or more ribbon counters on Prize Pig, remove those counters and untap it.${T}: Add one mana of any color.| +Travel Through Caradhras|Tales of Middle-earth Commander|127|R|{5}{G}|Sorcery|||Council's dilemma -- Starting with you, each player votes for Redhorn Pass or Mines of Moria. For each Redhorn Pass vote, search your library for a basic land card and put it onto the battlefield tapped. If you search your library this way, shuffle. For each Mines of Moria vote, return a card from your graveyard to your hand.$Exile Travel Through Caradhras.| +Cirdan the Shipwright|Tales of Middle-earth Commander|133|R|{3}{G}{U}|Legendary Creature - Elf Noble|3|4|Vigilance$Secret council -- Whenever Cirdan the Shipwright enters the battlefield or attacks, each player secretly votes for a player, then those votes are revealed. Each player draws a card for each vote they received. Each player who received no votes may put a permanent card from their hand onto the battlefield.| +Elrond of the White Council|Tales of Middle-earth Commander|134|R|{3}{G}{U}|Legendary Creature - Elf Noble|3|3|Secret council -- When Elrond of the White Council enters the battlefield, each player secretly votes for fellowship or aid, then those votes are revealed. For each fellowship vote, the voter chooses a creature they control. You gain control of each creature chosen this way, and they gain "This creature can't attack its owner." Then for each aid vote, put a +1/+1 counter on each creature you control.| +Eomer, King of Rohan|Tales of Middle-earth Commander|135|R|{3}{R}{W}|Legendary Creature - Human Noble|2|2|Double strike$Eomer, King of Rohan enters the battlefield with a +1/+1 counter on it for each other Human you control.$When Eomer enters the battlefield, target player becomes the monarch. Eomer deals damage equal to its power to any target.| +Forth Eorlingas!|Tales of Middle-earth Commander|139|R|{X}{R}{W}|Sorcery|||Create X 2/2 red Human Knight creature tokens with trample and haste.$Whenever one or more creatures you control deal combat damage to one or more players this turn, you become the monarch.| +Grima, Saruman's Footman|Tales of Middle-earth Commander|140|R|{2}{U}{B}|Legendary Creature - Human Advisor|1|4|Grima, Saruman's Footman can't be blocked.$Whenever Grima 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.| +Merry, Warden of Isengard|Tales of Middle-earth Commander|143|R|{1}{G}{W}|Legendary Creature - Halfling Advisor|1|4|Partner with Pippin, Warden of Isengard$Whenever one or more artifacts enter the battlefield under your control, create a 1/1 white Soldier creature token with lifelink. This ability triggers only once each turn.| +Sail into the West|Tales of Middle-earth Commander|149|R|{2}{G}{U}|Instant|||Will of the council -- Starting with you, each player votes for return or embark. If return gets more votes, each player returns up to two cards from their graveyard to their hand, then you exile Sail into the West. If embark gets more votes or the vote is tied, each player may discard their hand and draw seven cards.| +Treebeard, Gracious Host|Tales of Middle-earth Commander|153|R|{2}{G}{W}|Legendary Creature - Treefolk|0|5|Trample, ward {2}$When Treebeard, Gracious Host enters the battlefield, create two Food tokens.$Whenever you gain life, put that many +1/+1 counters on target Halfling or Treefolk.| +Wake the Dragon|Tales of Middle-earth Commander|154|R|{4}{B}{R}|Sorcery|||Create a 6/6 black and red Dragon creature token with flying, menace, and "Whenever this creature deals combat damage to a player, gain control of target artifact that player controls."$Flashback {6}{B}{R}| +The Black Gate|Tales of Middle-earth Commander|160|R||Legendary Land - Gate|||As The Black Gate enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped.${T}: Add {B}.${1}{B}, {T}: Choose a player with the most life or tied for most life. Target creature can't be blocked by creatures that player controls this turn.| Banishing Light|Tales of Middle-earth Commander|161|U|{2}{W}|Enchantment|||When Banishing Light enters the battlefield, exile target nonland permanent an opponent controls until Banishing Light leaves the battlefield.| Bastion Protector|Tales of Middle-earth Commander|162|R|{2}{W}|Creature - Human Soldier|3|3|Commander creatures you control get +2/+2 and have indestructible.| Call for Unity|Tales of Middle-earth Commander|163|R|{3}{W}{W}|Enchantment|||Revolt -- At the beginning of your end step, if a permanent you controlled left the battlefield this turn, put a unity counter on Call for Unity.$Creatures you control get +1/+1 for each unity counter on Call for Unity.| @@ -49288,7 +49287,7 @@ Woodfall Primus|Tales of Middle-earth Commander|264|R|{5}{G}{G}{G}|Creature - Tr Anguished Unmaking|Tales of Middle-earth Commander|265|R|{1}{W}{B}|Instant|||Exile target nonland permanent. You lose 3 life.| Extract from Darkness|Tales of Middle-earth Commander|266|U|{3}{U}{B}|Sorcery|||Each player mills two cards. Then you put a creature card from a graveyard onto the battlefield under your control.| Growth Spiral|Tales of Middle-earth Commander|267|C|{G}{U}|Instant|||Draw a card. You may put a land card from your hand onto the battlefield.| -Hostage Taker|Tales of Middle-earth Commander|268|R|{2}{U}{B}|Creature - Human Pirate|2|3|When Hostage Taker enters the battlefield, exile another target creature or artifact until Hostage Taker leaves the battlefield. You may cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any type to cast that spell.| +Hostage Taker|Tales of Middle-earth Commander|268|R|{2}{U}{B}|Creature - Human Pirate|2|3|When Hostage Taker enters the battlefield, exile another target creature or artifact until Hostage Taker leaves the battlefield. You may cast that card for as long as it remains exiled, and mana of any type can be spent to cast it.| Mortify|Tales of Middle-earth Commander|269|U|{1}{W}{B}|Instant|||Destroy target creature or enchantment.| Notion Thief|Tales of Middle-earth Commander|270|R|{2}{U}{B}|Creature - Human Rogue|3|1|Flash$If an opponent would draw a card except the first one they draw in each of their draw steps, instead that player skips that draw and you draw a card.| Savvy Hunter|Tales of Middle-earth Commander|271|U|{1}{B}{G}|Creature - Human Warrior|3|3|Whenever Savvy Hunter attacks or blocks, create a Food token.$Sacrifice two Foods: Draw a card.| @@ -49382,7 +49381,6 @@ Bojuka Bog|Tales of Middle-earth Commander|358|M||Land|||Bojuka Bog enters the b Boseiju, Who Shelters All|Tales of Middle-earth Commander|359|M||Legendary Land|||Boseiju, Who Shelters All enters the battlefield tapped.${T}, Pay 2 life: Add {C}. If that mana is spent on an instant or sorcery spell, that spell can't be countered.| Cabal Coffers|Tales of Middle-earth Commander|360|M||Land|||{2}, {T}: Add {B} for each Swamp you control.| Castle Ardenvale|Tales of Middle-earth Commander|361|M||Land|||Castle Ardenvale enters the battlefield tapped unless you control a Plains.${T}: Add {W}.${2}{W}{W}, {T}: Create a 1/1 white Human creature token.| -Cavern of Souls|Tales of Middle-earth Commander|362|M||Land|||As Cavern of Souls enters the battlefield, choose a creature type.${T}: Add {C}.${T}: Add one mana of any color. Spend this mana only to cast a creature spell of the chosen type, and that spell can't be countered.| Deserted Temple|Tales of Middle-earth Commander|363|M||Land|||{T}: Add {C}.${1}, {T}: Untap target land.| Gemstone Caverns|Tales of Middle-earth Commander|364|M||Legendary Land|||If Gemstone Caverns is in your opening hand and you're not the starting player, you may begin the game with Gemstone Caverns on the battlefield with a luck counter on it. If you do, exile a card from your hand.${T}: Add {C}. If Gemstone Caverns has a luck counter on it, instead add one mana of any color.| Homeward Path|Tales of Middle-earth Commander|365|M||Land|||{T}: Add {C}.${T}: Each player gains control of all creatures they own.| @@ -49398,6 +49396,79 @@ Shinka, the Bloodsoaked Keep|Tales of Middle-earth Commander|374|M||Legendary La Urborg, Tomb of Yawgmoth|Tales of Middle-earth Commander|375|M||Legendary Land|||Each land is a Swamp in addition to its other land types.| Wasteland|Tales of Middle-earth Commander|376|M||Land|||{T}: Add {C}.${T}, Sacrifice Wasteland: Destroy target nonbasic land.| Yavimaya, Cradle of Growth|Tales of Middle-earth Commander|377|M||Legendary Land|||Each land is a Forest in addition to its other land types.| +Cavern of Souls|Tales of Middle-earth Commander|392z|M||Land|||As Cavern of Souls enters the battlefield, choose a creature type.${T}: Add {C}.${T}: Add one mana of any color. Spend this mana only to cast a creature spell of the chosen type, and that spell can't be countered.| +The Gaffer|Tales of Middle-earth Commander|414|R|{2}{W}|Legendary Creature - Halfling Peasant|2|3|At the beginning of each end step, if you gained 3 or more life this turn, draw a card.| +Grey Host Reinforcements|Tales of Middle-earth Commander|416|R|{3}{W}|Creature - Spirit Soldier|1|1|Flying, ward {3}$When Grey Host Reinforcements enters the battlefield, exile target player's graveyard. Put a number of +1/+1 counters on Grey Host Reinforcements equal to the number of creature cards exiled this way.| +Archivist of Gondor|Tales of Middle-earth Commander|420|R|{2}{U}|Creature - Human Advisor|2|3|When your commander deals combat damage to a player, if there is no monarch, you become the monarch.$At the beginning of the monarch's end step, that player draws a card.| +Monstrosity of the Lake|Tales of Middle-earth Commander|424|R|{4}{U}|Legendary Creature - Kraken|4|6|When Monstrosity of the Lake enters the battlefield, you may pay {5}. If you do, tap all creatures your opponents control, then put a stun counter on each of those creatures.$Islandcycling {2}| +Gollum, Obsessed Stalker|Tales of Middle-earth Commander|428|R|{1}{B}|Legendary Creature - Halfling Horror|1|1|Skulk$At the beginning of your end step, each opponent dealt combat damage this game by a creature named Gollum, Obsessed Stalker loses life equal to the amount of life you gained this turn.| +Rampaging War Mammoth|Tales of Middle-earth Commander|436|R|{5}{R}{R}|Creature - Elephant|9|7|Trample$Cycling {X}{2}{R}$When you cycle Rampaging War Mammoth, destroy up to X target artifacts.| +Arwen, Weaver of Hope|Tales of Middle-earth Commander|437|R|{1}{G}{G}|Legendary Creature - Elf Noble|2|1|Each other creature you control enters the battlefield with a number of additional +1/+1 counters on it equal to Arwen, Weaver of Hope's toughness.| +Legolas Greenleaf|Tales of Middle-earth Commander|442|R|{2}{G}|Legendary Creature - Elf Archer|2|2|Reach$Legolas Greenleaf can't be blocked by creatures with power 2 or less.$Whenever another legendary creature enters the battlefield under your control, put a +1/+1 counter on Legolas Greenleaf.$Whenever Legolas Greenleaf deals combat damage to a player, draw a card.| +Mirkwood Elk|Tales of Middle-earth Commander|443|R|{5}{G}|Creature - Elk|6|6|Trample$Whenever Mirkwood Elk enters the battlefield or attacks, return target Elf card from your graveyard to your hand. You gain life equal to that card's power.| +Motivated Pony|Tales of Middle-earth Commander|444|R|{4}{G}|Creature - Horse|3|3|Trample, haste$Whenever Motivated Pony attacks, attacking creatures get +1/+1 until end of turn. If a Food entered the battlefield under your control this turn, untap those creatures and they get an additional +2/+2 until end of turn.| +Windswift Slice|Tales of Middle-earth Commander|447|R|{2}{G}|Instant|||Target creature you control deals damage equal to its power to target creature you don't control. Create a number of 1/1 green Elf Warrior creature tokens equal to the amount of excess damage dealt this way.| +Aragorn, King of Gondor|Tales of Middle-earth Commander|448|M|{1}{U}{R}{W}|Legendary Creature - Human Noble|4|4|Vigilance, lifelink$When Aragorn, King of Gondor enters the battlefield, you become the monarch.$Whenever Aragorn attacks, up to one target creature can't block this turn. If you're the monarch, creatures can't block this turn.| +Bilbo, Birthday Celebrant|Tales of Middle-earth Commander|451|R|{W}{B}{G}|Legendary Creature - Halfling Rogue|2|3|If you would gain life, you gain that much life plus 1 instead.${2}{W}{B}{G}, {T}, Exile Bilbo, Birthday Celebrant: Search your library for any number of creature cards, put them onto the battlefield, then shuffle. Activate only if you have 111 or more life.| +Boromir, Gondor's Hope|Tales of Middle-earth Commander|452|R|{2}{W}{U}|Legendary Creature - Human Warrior|3|4|Whenever Boromir, Gondor's Hope enters the battlefield or attacks, look at the top six cards of your library. You may reveal a Human or artifact card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.| +Erestor of the Council|Tales of Middle-earth Commander|457|R|{1}{G}{U}|Legendary Creature - Elf Noble|2|4|Whenever players finish voting, each opponent who voted for a choice you voted for creates a Treasure token. You scry X, where X is the number of opponents who voted for a choice you didn't vote for. Draw a card.| +Faramir, Steward of Gondor|Tales of Middle-earth Commander|458|R|{1}{W}{U}|Legendary Creature - Human Noble|2|2|Whenever a legendary creature with mana value 4 or greater enters the battlefield under your control, you become the monarch.$At the beginning of your end step, if you're the monarch, create two 1/1 white Human Soldier creature tokens.| +Farmer Cotton|Tales of Middle-earth Commander|459|R|{X}{G}{W}|Legendary Creature - Halfling Peasant|1|1|When Farmer Cotton enters the battlefield, create X 1/1 white Halfling creature tokens and X Food tokens.| +Lord of the Nazgul|Tales of Middle-earth Commander|467|R|{3}{U}{B}|Legendary Creature - Wraith Noble|4|3|Flying$Wraiths you control have protection from Ring-bearers.$Whenever you cast an instant or sorcery spell, create a 3/3 black Wraith creature token with menace. Then if you control nine or more Wraiths, Wraiths you control have base power and toughness 9/9 until end of turn.| +Oath of Eorl|Tales of Middle-earth Commander|471|R|{3}{R}{W}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Create two 1/1 white Human Soldier creature tokens.$II -- Create two 2/2 red Human Knight creature tokens with trample and haste.$III -- Put an indestructible counter on up to one target Human. You become the monarch.| +Riders of Rohan|Tales of Middle-earth Commander|474|R|{3}{R}{W}|Creature - Human Knight|4|4|When Riders of Rohan enters the battlefield, create two 2/2 red Human Knight creature tokens with trample and haste.$Dash {4}{R}{W}| +Sauron, Lord of the Rings|Tales of Middle-earth Commander|478|M|{5}{U}{B}{R}|Legendary Creature - Avatar Horror|9|9|When you cast this spell, amass Orcs 5, mill five cards, then return a creature card from your graveyard to the battlefield.$Trample$Whenever a commander an opponent controls dies, the Ring tempts you.| +Song of Earendil|Tales of Middle-earth Commander|479|R|{3}{G}{U}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Scry 2, then draw two cards.$II -- Create a Treasure token and a 2/2 blue Bird creature token with flying.$III -- Put a flying counter on each creature you control without flying.| +Summons of Saruman|Tales of Middle-earth Commander|480|R|{X}{U}{R}|Sorcery|||Amass Orcs X. Mill X cards. You may cast an instant or sorcery spell with mana value X or less from among them without paying its mana cost.$Flashback--{3}{U}{R}, Exile X cards from your graveyard.| +Taunt from the Rampart|Tales of Middle-earth Commander|481|R|{3}{R}{W}|Sorcery|||Goad all creatures your opponents control. Until your next turn, those creatures can't block.| +Hithlain Rope|Tales of Middle-earth Commander|486|R|{2}|Artifact|||Hithlain Rope can't be sacrificed.${1}, {T}: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle. The player to your right gains control of Hithlain Rope.${2}, {T}: Draw a card. The player to your right gains control of Hithlain Rope.| +Lothlorien Blade|Tales of Middle-earth Commander|487|R|{3}|Artifact - Equipment|||Whenever equipped creature attacks, it deals damage equal to its power to target creature defending player controls.$Equip Elf {2}$Equip {5}| +Model of Unity|Tales of Middle-earth Commander|488|R|{3}|Artifact|||Whenever players finish voting, you and each opponent who voted for a choice you voted for may scry 2.${T}: Add one mana of any color.| +Relic of Sauron|Tales of Middle-earth Commander|489|R|{4}|Artifact|||{T}: Add two mana in any combination of {U}, {B}, and/or {R}.${3}, {T}: Draw two cards, then discard a card.| +Anduril, Narsil Reforged|Tales of Middle-earth Commander|491|R|{2}|Legendary Artifact - Equipment|||Ascend$Whenever equipped creature attacks, put a +1/+1 counter on each creature you control. If you have the city's blessing, put two +1/+1 counters on each creature you control instead.$Equip {3}| +Aragorn, Hornburg Hero|Tales of Middle-earth Commander|492|M|{1}{R}{G}{W}|Legendary Creature - Human Soldier|4|4|Attacking creatures you control have first strike and renown 1.$Whenever a renowned creature you control deals combat damage to a player, double the number of +1/+1 counters on it.| +Legolas's Quick Reflexes|Tales of Middle-earth Commander|493|R|{G}|Instant|||Split second$Untap target creature. Until end of turn, it gains hexproof, reach, and "Whenever this creature becomes tapped, it deals damage equal to its power to up to one target creature."| +Galadriel, Light of Valinor|Tales of Middle-earth Commander|498|M|{2}{G}{W}{U}|Legendary Creature - Elf Noble|3|3|Alliance -- Whenever another creature enters the battlefield under your control, choose one that hasn't been chosen this turn --$* Add {G}{G}{G}.$* Put a +1/+1 counter on each creature you control.$* Scry 2, then draw a card.| +Rally the Galadhrim|Tales of Middle-earth Commander|499|R|{2}{G}{U}|Sorcery|||Create a token that's a copy of target creature you control.$Conspire| +Galadhrim Brigade|Tales of Middle-earth Commander|502|R|{2}{G}|Creature - Elf Soldier|2|2|Squad {1}{G}$Other Elves you control get +1/+1.| +Olorin's Searing Light|Tales of Middle-earth Commander|503|R|{2}{R}{W}|Instant|||Each opponent exiles a creature with the greatest power among creatures that player controls.$Spell mastery -- If there are two or more instant and/or sorcery cards in your graveyard, Olorin's Searing Light deals damage to each opponent equal to the power of the creature they exiled.| +Sorcerous Squall|Tales of Middle-earth Commander|504|R|{6}{U}{U}{U}|Sorcery|||Delve$Target opponent mills nine cards, then you may cast an instant or sorcery spell from that player's graveyard without paying its mana cost. If that spell would be put into a graveyard, exile it instead.| +Courageous Resolve|Tales of Middle-earth Commander|506|R|{2}{W}|Instant|||Up to one target creature you control gains protection from each of your opponents until end of turn. Draw a card.$Fateful hour -- If you have 5 or less life, you can't lose life this turn, you can't lose the game this turn, and your opponents can't win the game this turn.| +Gandalf of the Secret Fire|Tales of Middle-earth Commander|507|M|{1}{U}{R}{W}|Legendary Creature - Avatar Wizard|3|4|Whenever you cast an instant or sorcery spell from your hand during an opponent's turn, exile that card with three time counters on it instead of putting it into your graveyard as it resolves. Then if the exiled card doesn't have suspend, it gains suspend.| +Call Forth the Tempest|Tales of Middle-earth Commander|509|R|{5}{R}{R}{R}|Sorcery|||Cascade, cascade$Call Forth the Tempest deals damage to each creature your opponents control equal to the total mana value of other spells you've cast this turn.| +Witch-king, Sky Scourge|Tales of Middle-earth Commander|511|M|{5}{B}{R}|Legendary Creature - Wraith Noble|5|5|Flying$Whenever you attack with one or more Wraiths, exile the top X cards of your library, where X is their total power. You may play those cards this turn.$Undying| +Fell Beast of Mordor|Tales of Middle-earth Commander|513|R|{2}{B}{B}|Creature - Drake Beast|3|3|Flying$Devour 1$Whenever Fell Beast of Mordor enters the battlefield or attacks, target opponent loses X life and you gain X life, where X is the number of +1/+1 counters on it.| +Minas Morgul, Dark Fortress|Tales of Middle-earth Commander|514|R||Legendary Land|||Minas Morgul, Dark Fortress enters the battlefield tapped.${T}: Add {B}.${3}{B}, {T}: Put a shadow counter on target creature. For as long as that creature has a shadow counter on it, it's a Wraith in addition to its other types.| +Kenrith, the Returned King|Tales of Middle-earth Commander|515|R|{4}{W}|Legendary Creature - Human Noble|5|5|{R}: All creatures gain trample and haste until end of turn.${1}{G}: Put a +1/+1 counter on target creature.${2}{W}: Target player gains 5 life.${3}{U}: Target player draws a card.${4}{B}: Put target creature card from a graveyard onto the battlefield under its owner's control.| +Ishkanah, Grafwidow|Tales of Middle-earth Commander|516|M|{4}{G}|Legendary Creature - Spider|3|5|Reach$Delirium -- When Ishkanah, Grafwidow enters the battlefield, if there are four or more card types among cards in your graveyard, create three 1/2 green Spider creature tokens with reach.${6}{B}: Target opponent loses 1 life for each Spider you control.| +Doran, the Siege Tower|Tales of Middle-earth Commander|517|R|{W}{B}{G}|Legendary Creature - Treefolk Shaman|0|5|Each creature assigns combat damage equal to its toughness rather than its power.| +Hammerheim|Tales of Middle-earth Commander|518|R||Legendary Land|||{T}: Add {R}.${T}: Target creature loses all landwalk abilities until end of turn.| +Urborg|Tales of Middle-earth Commander|519|R||Legendary Land|||{T}: Add {B}.${T}: Target creature loses first strike or swampwalk until end of turn.| +Soul's Attendant|Tales of Middle-earth Commander|520|U|{W}|Creature - Human Cleric|1|1|Whenever another creature enters the battlefield, you may gain 1 life.| +Stonehewer Giant|Tales of Middle-earth Commander|521|R|{3}{W}{W}|Creature - Giant Warrior|4|4|Vigilance${1}{W}, {T}: Search your library for an Equipment card, put it onto the battlefield, attach it to a creature you control, then shuffle.| +Worship|Tales of Middle-earth Commander|522|R|{3}{W}|Enchantment|||If you control a creature, damage that would reduce your life total to less than 1 reduces it to 1 instead.| +Pact of Negation|Tales of Middle-earth Commander|523|R|{0}|Instant|||Counter target spell.$At the beginning of your next upkeep, pay {3}{U}{U}. If you don't, you lose the game.| +River Kelpie|Tales of Middle-earth Commander|524|R|{3}{U}{U}|Creature - Beast|3|3|Whenever River Kelpie or another permanent enters the battlefield from a graveyard, draw a card.$Whenever a player casts a spell from a graveyard, draw a card.$Persist| +Abyssal Persecutor|Tales of Middle-earth Commander|525|M|{2}{B}{B}|Creature - Demon|6|6|Flying, trample$You can't win the game and your opponents can't lose the game.| +Diabolic Intent|Tales of Middle-earth Commander|526|M|{1}{B}|Sorcery|||As an additional cost to cast this spell, sacrifice a creature.$Search your library for a card, put that card into your hand, then shuffle.| +Elvish Harbinger|Tales of Middle-earth Commander|527|U|{2}{G}|Creature - Elf Druid|1|2|When Elvish Harbinger enters the battlefield, you may search your library for an Elf card, reveal it, then shuffle and put that card on top.${T}: Add one mana of any color.| +Explore|Tales of Middle-earth Commander|528|U|{1}{G}|Sorcery|||You may play an additional land this turn.$Draw a card.| +Seasons Past|Tales of Middle-earth Commander|529|M|{4}{G}{G}|Sorcery|||Return any number of cards with different mana values from your graveyard to your hand. Put Seasons Past on the bottom of its owner's library.| +Second Harvest|Tales of Middle-earth Commander|530|R|{2}{G}{G}|Instant|||For each token you control, create a token that's a copy of that permanent.| +Sylvan Tutor|Tales of Middle-earth Commander|531|M|{G}|Sorcery|||Search your library for a creature card, reveal it, then shuffle and put that card on top.| +Tempt with Discovery|Tales of Middle-earth Commander|532|U|{3}{G}|Sorcery|||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.| +Timber Protector|Tales of Middle-earth Commander|533|R|{4}{G}|Creature - Treefolk Warrior|4|6|Other Treefolk creatures you control get +1/+1.$Other Treefolk and Forests you control have indestructible.| +Myriad Landscape|Tales of Middle-earth Commander|534|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.| +Gimli's Reckless Might|Tales of Middle-earth Commander|538|R|{3}{R}|Enchantment|||Creatures you control have haste.$Formidable -- Whenever you attack, if creatures you control have total power 8 or greater, target attacking creature you control fights up to one target creature you don't control.| +Isengard Unleashed|Tales of Middle-earth Commander|539|R|{2}{R}{R}{R}|Sorcery|||Damage can't be prevented this turn. If a source you control would deal damage this turn to an opponent or a permanent an opponent controls, it deals triple that damage instead.$Flashback {4}{R}{R}{R}| +Rohirrim Chargers|Tales of Middle-earth Commander|540|R|{2}{R}{W}|Creature - Human Knight|4|4|You may exert Rohirrim Chargers as it attacks.$Whenever you exert a creature, reveal cards from the top of your library until you reveal an Equipment card. Put that card onto the battlefield attached to that creature, then put the rest on the bottom of your library in a random order.| +Arboreal Alliance|Tales of Middle-earth Commander|541|R|{X}{G}{G}|Enchantment|||When Arboreal Alliance enters the battlefield, create an X/X green Treefolk creature token.$Whenever you attack with one or more Elves, populate.| +Galadriel's Dismissal|Tales of Middle-earth Commander|544|R|{W}|Instant|||Kicker {2}{W}$Target creature phases out. If this spell was kicked, each creature target player controls phases out instead.| +Mists of Lorien|Tales of Middle-earth Commander|545|R|{2}{U}|Sorcery|||Replicate {U}$Return target nonland permanent and each other nonland permanent with the same mana value as that permanent to their owners' hands.| +Rammas Echor, Ancient Shield|Tales of Middle-earth Commander|549|R|{3}{W}|Legendary Artifact|||Whenever you cast your second spell each turn, draw a card, then create a 0/3 white Wall creature token with defender.$At the beginning of combat on your turn, creatures you control with defender gain exalted until end of turn.| +Fell Beast's Shriek|Tales of Middle-earth Commander|552|R|{U}{R}|Sorcery|||Each opponent chooses a creature they control. Tap and goad the chosen creatures.$Splice onto instant or sorcery {2}{U}{R}| +Nazgul Battle-Mace|Tales of Middle-earth Commander|554|R|{5}|Artifact - Equipment|||Equipped creature has menace, deathtouch, annihilator 1, and "Whenever an opponent sacrifices a nontoken permanent, put that card onto the battlefield under your control unless that player pays 3 life."$Equip {3}| +Mordor on the March|Tales of Middle-earth Commander|556|R|{3}{B}{R}|Sorcery|||Exile a creature card from your graveyard. Create a token that's a copy of it. It gains haste until end of turn. Exile it at the beginning of the next end step.$Storm| The Prismatic Piper|Commander Masters|1|S|{5}|Legendary Creature - Shapeshifter|3|3|If The Prismatic Piper is your commander, choose a color before the game begins. The Prismatic Piper is the chosen color.$Partner| Kozilek, the Great Distortion|Commander Masters|2|M|{8}{C}{C}|Legendary Creature - Eldrazi|12|12|When you cast this spell, if you have fewer than seven cards in hand, draw cards equal to the difference.$Menace$Discard a card with mana value X: Counter target spell with mana value X.| Morophon, the Boundless|Commander Masters|3|M|{7}|Legendary Creature - Shapeshifter|6|6|Changeling$As Morophon, the Boundless enters the battlefield, choose a creature type.$Spells of the chosen type you cast cost {W}{U}{B}{R}{G} less to cast. This effect reduces only the amount of colored mana you pay.$Other creatures you control of the chosen type get +1/+1.| @@ -50506,8 +50577,8 @@ Archmage of Echoes|Wilds of Eldraine Commander|9|R|{4}{U}|Creature - Faerie Wiza Malleable Impostor|Wilds of Eldraine Commander|10|R|{3}{U}|Creature - Faerie Shapeshifter|0|0|Flash$Flying$You may have Malleable Impostor enter the battlefield as a copy of a creature an opponent controls, except it's a Faerie Shapeshifter in addition to its other types and it has flying.| Misleading Signpost|Wilds of Eldraine Commander|11|R|{2}{U}|Artifact|||Flash$When Misleading Signpost enters the battlefield during the declare attackers step, you may reselect which player or permanent target attacking creature is attacking.${T}: Add {U}.| Shadow Puppeteers|Wilds of Eldraine Commander|12|R|{6}{U}|Creature - Faerie Wizard|4|4|Flying, ward {2}$When Shadow Puppeteers enters the battlefield, create two 1/1 black Faerie Rogue creature tokens with flying.$Whenever a creature you control with flying attacks, you may have it become a red Dragon with base power and toughness 4/4 in addition to its other colors and types until end of turn.| -Blightwing Bandit|Wilds of Eldraine Commander|13|R|{3}{B}|Creature - Faerie Rogue|2|2|Flying, deathtouch$ Whenever you cast your first spell during each opponent's turn, look at the top card of that player's library, then exile it face down. You may play that card for as long as it remains exiled, and mana of any type can be spent to cast it.| -Faerie Bladecrafter|Wilds of Eldraine Commander|14|R|{2}{B}|Creature - Faerie Rogue|2|2|Flying$Whenever one or more Faeries you control deal combat damage to a player, put a +1/+1 counter on Faerie Bladecrafter.$ When Faerie Bladecrafter dies, each opponent loses X life and you gain X life, where X is its power.| +Blightwing Bandit|Wilds of Eldraine Commander|13|R|{3}{B}|Creature - Faerie Rogue|2|2|Flying, deathtouch$Whenever you cast your first spell during each opponent's turn, look at the top card of that player's library, then exile it face down. You may play that card for as long as it remains exiled, and mana of any type can be spent to cast it.| +Faerie Bladecrafter|Wilds of Eldraine Commander|14|R|{2}{B}|Creature - Faerie Rogue|2|2|Flying$Whenever one or more Faeries you control deal combat damage to a player, put a +1/+1 counter on Faerie Bladecrafter.$When Faerie Bladecrafter dies, each opponent loses X life and you gain X life, where X is its power.| Nettling Nuisance|Wilds of Eldraine Commander|15|R|{2}{B}|Creature - Faerie Rogue|3|1|Flying$Whenever one or more Faeries you control deal combat damage to a player, that player creates a 4/2 red Pirate creature token with "This creature can't block." The token is goaded for the rest of the game.| Tegwyll's Scouring|Wilds of Eldraine Commander|16|R|{4}{B}{B}|Sorcery|||You may cast Tegwyll's Scouring as though it had flash by tapping three untapped creatures you control with flying in addition to paying its other costs.$Destroy all creatures. Create three 1/1 black Faerie Rogue creature tokens with flying.| Giant Inheritance|Wilds of Eldraine Commander|17|R|{4}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +5/+5 and has "Whenever this creature attacks, create a Monster Role token attached to up to one target attacking creature."$When Giant Inheritance is put into a graveyard from the battlefield, return it to its owner's hand.| @@ -50517,8 +50588,126 @@ Timber Paladin|Wilds of Eldraine Commander|20|R|{1}{G}|Artifact Creature - Knigh Court of Ardenvale|Wilds of Eldraine Commander|21|R|{2}{W}{W}|Enchantment|||When Court of Ardenvale enters the battlefield, you become the monarch.$At the beginning of your upkeep, return target permanent card with mana value 3 or less from your graveyard to your hand. If you're the monarch, return that permanent card to the battlefield instead.| Court of Vantress|Wilds of Eldraine Commander|22|R|{2}{U}{U}|Enchantment|||When Court of Vantress enters the battlefield, you become the monarch.$At the beginning of your upkeep, choose up to one other target enchantment or artifact. If you're the monarch, you may create a token that's a copy of it. If you're not the monarch, you may have Court of Vantress become a copy of it, except it has this ability.| Court of Locthwain|Wilds of Eldraine Commander|23|R|{2}{B}{B}|Enchantment|||When Court of Locthwain enters the battlefield, you become the monarch.$At the beginning of your upkeep, exile the top card of target opponent's library. You may play that card for as long as it remains exiled, and mana of any type can be spent to cast it. If you're the monarch, until end of turn, you may cast a spell from among cards exiled with Court of Locthwain without paying its mana cost.| -Court of Embereth|Wilds of Eldraine Commander|24|R|{2}{R}{R}|Enchantment|||When Court of Embereth enters the battlefield, you become the monarch.$ At the beginning of your upkeep, create a 3/1 red Knight creature token. Then if you're the monarch, Court of Embereth deals X damage to each opponent, where X is the number of creatures you control.| +Court of Embereth|Wilds of Eldraine Commander|24|R|{2}{R}{R}|Enchantment|||When Court of Embereth enters the battlefield, you become the monarch.$At the beginning of your upkeep, create a 3/1 red Knight creature token. Then if you're the monarch, Court of Embereth deals X damage to each opponent, where X is the number of creatures you control.| Court of Garenbrig|Wilds of Eldraine Commander|25|R|{1}{G}{G}|Enchantment|||When Court of Garenbrig enters the battlefield, you become the monarch.$At the beginning of your upkeep, distribute two +1/+1 counters among up to two target creatures. Then if you're the monarch, double the number of +1/+1 counters on each creature you control.| Korvold, Gleeful Glutton|Wilds of Eldraine Commander|26|M|{5}{B}{R}{G}|Legendary Creature - Dragon Noble|4|4|This spell costs {1} less to cast for each card type among permanents you've sacrificed this turn.$Flying, trample, haste$Whenever Korvold deals combat damage to a player, put X +1/+1 counters on Korvold and draw X cards, where X is the number of permanent types among cards in your graveyard.| -Brenard, Ginger Sculptor|Wilds of Eldraine Commander|27|M|{1}{G}{W}{U}|Legendary Creature - Human Artificer|3|3|Each creature you control that's a Food or a Golem gets +2/+2 and has trample.$Whenever another nontoken creature you control dies, you may exile it. If you do, create a token that's a copy of that creature, except it's a 1/1 Food Golem artifact creature in addition to its other types and it has "2, {T}, Sacrifice this artifact: You gain 3 life."| +Brenard, Ginger Sculptor|Wilds of Eldraine Commander|27|M|{1}{G}{W}{U}|Legendary Creature - Human Artificer|3|3|Each creature you control that's a Food or a Golem gets +2/+2 and has trample.$Whenever another nontoken creature you control dies, you may exile it. If you do, create a token that's a copy of that creature, except it's a 1/1 Food Golem artifact creature in addition to its other types and it has "{2}, {T}, Sacrifice this artifact: You gain 3 life."| Throne of Eldraine|Wilds of Eldraine Commander|28|R|{5}|Legendary Artifact|||As Throne of Eldraine enters the battlefield, choose a color.${T}: Add four mana of the chosen color. Spend this mana only to cast monocolored spells of that color.${3}, {T}: Draw two cards. Spend only mana of the chosen color to activate this ability.| +Ajani's Chosen|Wilds of Eldraine Commander|59|R|{2}{W}{W}|Creature - Cat Soldier|3|3|Whenever an enchantment enters the battlefield under your control, create a 2/2 white Cat creature token. If that enchantment is an Aura, you may attach it to the token.| +Angelic Destiny|Wilds of Eldraine Commander|60|M|{2}{W}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +4/+4, has flying and first strike, and is an Angel in addition to its other types.$When enchanted creature dies, return Angelic Destiny to its owner's hand.| +Archon of Sun's Grace|Wilds of Eldraine Commander|61|R|{2}{W}{W}|Creature - Archon|3|4|Flying, lifelink$Pegasus creatures you control have lifelink.$Constellation -- Whenever an enchantment enters the battlefield under your control, create a 2/2 white Pegasus creature token with flying.| +Austere Command|Wilds of Eldraine Commander|62|R|{4}{W}{W}|Sorcery|||Choose two --$* Destroy all artifacts.$* Destroy all enchantments.$* Destroy all creatures with mana value 3 or less.$* Destroy all creatures with mana value 4 or greater.| +Celestial Archon|Wilds of Eldraine Commander|63|R|{3}{W}{W}|Enchantment Creature - Archon|4|4|Bestow {5}{W}{W}$Flying, first strike$Enchanted creature gets +4/+4 and has flying and first strike.| +Danitha Capashen, Paragon|Wilds of Eldraine Commander|64|U|{2}{W}|Legendary Creature - Human Knight|2|2|First strike, vigilance, lifelink$Aura and Equipment spells you cast cost {1} less to cast.| +Daybreak Coronet|Wilds of Eldraine Commander|65|R|{W}{W}|Enchantment - Aura|||Enchant creature with another Aura attached to it$Enchanted creature gets +3/+3 and has first strike, vigilance, and lifelink.| +Eidolon of Countless Battles|Wilds of Eldraine Commander|66|R|{1}{W}{W}|Enchantment Creature - Spirit|0|0|Bestow {2}{W}{W}$Eidolon of Countless Battles and enchanted creature each get +1/+1 for each creature you control and +1/+1 for each Aura you control.| +Ethereal Armor|Wilds of Eldraine Commander|67|C|{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 for each enchantment you control and has first strike.| +Generous Gift|Wilds of Eldraine Commander|68|U|{2}{W}|Instant|||Destroy target permanent. Its controller creates a 3/3 green Elephant creature token.| +Kor Spiritdancer|Wilds of Eldraine Commander|69|R|{1}{W}|Creature - Kor Wizard|0|2|Kor Spiritdancer gets +2/+2 for each Aura attached to it.$Whenever you cast an Aura spell, you may draw a card.| +Mantle of the Ancients|Wilds of Eldraine Commander|70|R|{3}{W}{W}|Enchantment - Aura|||Enchant creature you control$When Mantle of the Ancients enters the battlefield, return any number of target Aura and/or Equipment cards that could be attached to enchanted creature from your graveyard to the battlefield attached to enchanted creature.$Enchanted creature gets +1/+1 for each Aura and Equipment attached to it.| +Realm-Cloaked Giant|Wilds of Eldraine Commander|71|M|{5}{W}{W}|Creature - Giant|7|7|Vigilance| +Cast Off|Wilds of Eldraine Commander|71|M|{3}{W}{W}|Sorcery - Adventure|7|7|Destroy all non-Giant creatures.| +Retether|Wilds of Eldraine Commander|72|R|{3}{W}|Sorcery|||Return each Aura card from your graveyard to the battlefield. Only creatures can be enchanted this way.| +Sage's Reverie|Wilds of Eldraine Commander|73|U|{3}{W}|Enchantment - Aura|||Enchant creature$When Sage's Reverie enters the battlefield, draw a card for each Aura you control that's attached to a creature.$Enchanted creature gets +1/+1 for each Aura you control that's attached to a creature.| +Shalai, Voice of Plenty|Wilds of Eldraine Commander|74|R|{3}{W}|Legendary Creature - Angel|3|4|Flying$You, planeswalkers you control, and other creatures you control have hexproof.${4}{G}{G}: Put a +1/+1 counter on each creature you control.| +Spectral Steel|Wilds of Eldraine Commander|75|U|{1}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2.${1}{W}, Exile Spectral Steel from your graveyard: Return another target Aura or Equipment card from your graveyard to your hand.| +Starfield Mystic|Wilds of Eldraine Commander|76|R|{1}{W}|Creature - Human Cleric|2|2|Enchantment spells you cast cost {1} less to cast.$Whenever an enchantment you control is put into a graveyard from the battlefield, put a +1/+1 counter on Starfield Mystic.| +Sun Titan|Wilds of Eldraine Commander|77|M|{4}{W}{W}|Creature - Giant|6|6|Vigilance$Whenever Sun Titan enters the battlefield or attacks, you may return target permanent card with mana value 3 or less from your graveyard to the battlefield.| +Swords to Plowshares|Wilds of Eldraine Commander|78|U|{W}|Instant|||Exile target creature. Its controller gains life equal to its power.| +Timely Ward|Wilds of Eldraine Commander|79|R|{2}{W}|Enchantment - Aura|||You may cast Timely Ward as though it had flash if it targets a commander.$Enchant creature$Enchanted creature has indestructible.| +Tithe Taker|Wilds of Eldraine Commander|80|R|{1}{W}|Creature - Human Soldier|2|1|During your turn, spells your opponents cast cost {1} more to cast and abilities your opponents activate cost {1} more to activate unless they're mana abilities.$Afterlife 1| +Transcendent Envoy|Wilds of Eldraine Commander|81|C|{1}{W}|Enchantment Creature - Griffin|1|2|Flying$Aura spells you cast cost {1} less to cast.| +Umbra Mystic|Wilds of Eldraine Commander|82|R|{2}{W}|Creature - Human Wizard|2|2|Auras attached to permanents you control have totem armor.| +Winds of Rath|Wilds of Eldraine Commander|83|R|{3}{W}{W}|Sorcery|||Destroy all creatures that aren't enchanted. They can't be regenerated.| +Arcane Denial|Wilds of Eldraine Commander|84|C|{1}{U}|Instant|||Counter target spell. Its controller may draw up to two cards at the beginning of the next turn's upkeep.$You draw a card at the beginning of the next turn's upkeep.| +Brazen Borrower|Wilds of Eldraine Commander|85|M|{1}{U}{U}|Creature - Faerie Rogue|3|1|Flash$Flying$Brazen Borrower can block only creatures with flying.| +Petty Theft|Wilds of Eldraine Commander|85|M|{1}{U}|Instant - Adventure|3|1|Return target nonland permanent an opponent controls to its owner's hand.| +Cloud of Faeries|Wilds of Eldraine Commander|86|C|{1}{U}|Creature - Faerie|1|1|Flying$When Cloud of Faeries enters the battlefield, untap up to two lands.$Cycling {2}| +Consider|Wilds of Eldraine Commander|87|C|{U}|Instant|||Surveil 1.$Draw a card.| +Dig Through Time|Wilds of Eldraine Commander|88|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.| +Distant Melody|Wilds of Eldraine Commander|89|C|{3}{U}|Sorcery|||Choose a creature type. Draw a card for each permanent you control of that type.| +Fact or Fiction|Wilds of Eldraine Commander|90|U|{3}{U}|Instant|||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.| +Faerie Formation|Wilds of Eldraine Commander|91|R|{4}{U}|Creature - Faerie|5|4|Flying${3}{U}: Create a 1/1 blue Faerie creature token with flying. Draw a card.| +Faerie Seer|Wilds of Eldraine Commander|92|C|{U}|Creature - Faerie Wizard|1|1|Flying$When Faerie Seer enters the battlefield, scry 2.| +Frantic Search|Wilds of Eldraine Commander|93|C|{2}{U}|Instant|||Draw two cards, then discard two cards. Untap up to three lands.| +Glen Elendra Archmage|Wilds of Eldraine Commander|94|R|{3}{U}|Creature - Faerie Wizard|2|2|Flying${U}, Sacrifice Glen Elendra Archmage: Counter target noncreature spell.$Persist| +Hullbreaker Horror|Wilds of Eldraine Commander|95|R|{5}{U}{U}|Creature - Kraken Horror|7|8|Flash$This spell can't be countered.$Whenever you cast a spell, choose up to one --$* Return target spell you don't control to its owner's hand.$* Return target nonland permanent to its owner's hand.| +Hypnotic Sprite|Wilds of Eldraine Commander|96|U|{U}{U}|Creature - Faerie|2|1|Flying| +Mesmeric Glare|Wilds of Eldraine Commander|96|U|{2}{U}|Instant - Adventure|2|1|Counter target spell with mana value 3 or less.| +Illusionist's Gambit|Wilds of Eldraine Commander|97|R|{2}{U}{U}|Instant|||Cast this spell only during the declare blockers step on an opponent's turn.$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.| +Keep Watch|Wilds of Eldraine Commander|98|C|{2}{U}|Instant|||Draw a card for each attacking creature.| +Midnight Clock|Wilds of Eldraine Commander|99|R|{2}{U}|Artifact|||{T}: Add {U}.${2}{U}: Put an hour counter on Midnight Clock.$At the beginning of each upkeep, put an hour counter on Midnight Clock.$When the twelfth hour counter is put on Midnight Clock, shuffle your hand and graveyard into your library, then draw seven cards. Exile Midnight Clock.| +Nightveil Sprite|Wilds of Eldraine Commander|100|U|{1}{U}|Creature - Faerie Rogue|1|2|Flying$Whenever Nightveil Sprite attacks, surveil 1.| +Opt|Wilds of Eldraine Commander|101|C|{U}|Instant|||Scry 1.$Draw a card.| +Perplexing Test|Wilds of Eldraine Commander|102|R|{3}{U}{U}|Instant|||Choose one --$* Return all creature tokens to their owners' hands.$* Return all nontoken creatures to their owners' hands.| +Quickling|Wilds of Eldraine Commander|103|U|{1}{U}|Creature - Faerie Rogue|2|2|Flash$Flying$When Quickling enters the battlefield, sacrifice it unless you return another creature you control to its owner's hand.| +Reality Shift|Wilds of Eldraine Commander|104|U|{1}{U}|Instant|||Exile target creature. Its controller manifests the top card of their library.| +Reconnaissance Mission|Wilds of Eldraine Commander|105|U|{2}{U}{U}|Enchantment|||Whenever a creature you control deals combat damage to a player, you may draw a card.$Cycling {2}| +Reflections of Littjara|Wilds of Eldraine Commander|106|R|{4}{U}|Enchantment|||As Reflections of Littjara enters the battlefield, choose a creature type.$Whenever you cast a spell of the chosen type, copy that spell.| +Repulse|Wilds of Eldraine Commander|107|C|{2}{U}|Instant|||Return target creature to its owner's hand.$Draw a card.| +Run Away Together|Wilds of Eldraine Commander|108|C|{1}{U}|Instant|||Choose two target creatures controlled by different players. Return those creatures to their owners' hands.| +Scion of Oona|Wilds of Eldraine Commander|109|R|{2}{U}|Creature - Faerie Soldier|1|1|Flash$Flying$Other Faerie creatures you control get +1/+1.$Other Faeries you control have shroud.| +Snap|Wilds of Eldraine Commander|110|C|{1}{U}|Instant|||Return target creature to its owner's hand. Untap up to two lands.| +Sower of Temptation|Wilds of Eldraine Commander|111|R|{2}{U}{U}|Creature - Faerie Wizard|2|2|Flying$When Sower of Temptation enters the battlefield, gain control of target creature for as long as Sower of Temptation remains on the battlefield.| +Theoretical Duplication|Wilds of Eldraine Commander|112|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.| +Kindred Dominance|Wilds of Eldraine Commander|113|R|{5}{B}{B}|Sorcery|||Choose a creature type. Destroy all creatures that aren't of the chosen type.| +Nightmare Unmaking|Wilds of Eldraine Commander|114|R|{3}{B}{B}|Sorcery|||Choose one --$* Exile each creature with power greater than the number of cards in your hand.$* Exile each creature with power less than the number of cards in your hand.| +Puppeteer Clique|Wilds of Eldraine Commander|115|R|{3}{B}{B}|Creature - Faerie Wizard|3|2|Flying$When Puppeteer Clique enters the battlefield, put target creature card from an opponent's graveyard onto the battlefield under your control. It gains haste. At the beginning of your next end step, exile it.$Persist| +Rankle, Master of Pranks|Wilds of Eldraine Commander|116|R|{2}{B}{B}|Legendary Creature - Faerie Rogue|3|3|Flying, haste$Whenever Rankle, Master of Pranks deals combat damage to a player, choose any number --$* Each player discards a card.$* Each player loses 1 life and draws a card.$* Each player sacrifices a creature.| +Reckless Spite|Wilds of Eldraine Commander|117|U|{1}{B}{B}|Instant|||Destroy two target nonblack creatures. You lose 5 life.| +Thrilling Encore|Wilds of Eldraine Commander|118|R|{4}{B}|Instant|||Put onto the battlefield under your control all creature cards in all graveyards that were put there from the battlefield this turn.| +Ancestral Mask|Wilds of Eldraine Commander|119|C|{2}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 for each other enchantment on the battlefield.| +Aura Gnarlid|Wilds of Eldraine Commander|120|C|{2}{G}|Creature - Beast|2|2|Creatures with power less than Aura Gnarlid's power can't block it.$Aura Gnarlid gets +1/+1 for each Aura on the battlefield.| +Bear Umbra|Wilds of Eldraine Commander|121|R|{2}{G}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +2/+2 and has "Whenever this creature attacks, untap all lands you control."$Totem armor| +Careful Cultivation|Wilds of Eldraine Commander|122|C|{2}{G}|Enchantment - Aura|||Enchant artifact or creature$As long as enchanted permanent is a creature, it gets +1/+3 and has reach and "{T}: Add {G}{G}."$Channel -- {1}{G}, Discard Careful Cultivation: Create a 1/1 green Human Monk creature token with "{T}: Add {G}."| +Destiny Spinner|Wilds of Eldraine Commander|123|U|{1}{G}|Enchantment Creature - Human|2|3|Creature and enchantment spells you control can't be countered.${3}{G}: Target land you control becomes an X/X Elemental creature with trample and haste until end of turn, where X is the number of enchantments you control. It's still a land.| +Eidolon of Blossoms|Wilds of Eldraine Commander|124|R|{2}{G}{G}|Enchantment Creature - Spirit|2|2|Constellation -- Whenever Eidolon of Blossoms or another enchantment enters the battlefield under your control, draw a card.| +Enchantress's Presence|Wilds of Eldraine Commander|125|R|{2}{G}|Enchantment|||Whenever you cast an enchantment spell, draw a card.| +Fertile Ground|Wilds of Eldraine Commander|126|C|{1}{G}|Enchantment - Aura|||Enchant land$Whenever enchanted land is tapped for mana, its controller adds an additional one mana of any color.| +Indomitable Might|Wilds of Eldraine Commander|127|R|{3}{G}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature gets +3/+3.$Enchanted creature's controller may have it assign its combat damage as though it weren't blocked.| +Kenrith's Transformation|Wilds of Eldraine Commander|128|U|{1}{G}|Enchantment - Aura|||Enchant creature$When Kenrith's Transformation enters the battlefield, draw a card.$Enchanted creature loses all abilities and is a green Elk creature with base power and toughness 3/3.| +Paradise Druid|Wilds of Eldraine Commander|129|U|{1}{G}|Creature - Elf Druid|2|1|Paradise Druid has hexproof as long as it's untapped.${T}: Add one mana of any color.| +Rishkar's Expertise|Wilds of Eldraine Commander|130|R|{4}{G}{G}|Sorcery|||Draw cards equal to the greatest power among creatures you control.$You may cast a spell with mana value 5 or less from your hand without paying its mana cost.| +Sanctum Weaver|Wilds of Eldraine Commander|131|R|{1}{G}|Enchantment Creature - Dryad|0|2|{T}: Add X mana of any one color, where X is the number of enchantments you control.| +Setessan Champion|Wilds of Eldraine Commander|132|R|{2}{G}|Creature - Human Warrior|1|3|Constellation -- Whenever an enchantment enters the battlefield under your control, put a +1/+1 counter on Setessan Champion and draw a card.| +Snake Umbra|Wilds of Eldraine Commander|133|U|{2}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 and has "Whenever this creature deals damage to an opponent, you may draw a card."$Totem armor| +Sylvan Ranger|Wilds of Eldraine Commander|134|C|{1}{G}|Creature - Elf Scout Ranger|1|1|When Sylvan Ranger enters the battlefield, you may search your library for a basic land card, reveal it, put it into your hand, then shuffle.| +Utopia Sprawl|Wilds of Eldraine Commander|135|C|{G}|Enchantment - Aura|||Enchant Forest$As Utopia Sprawl enters the battlefield, choose a color.$Whenever enchanted Forest is tapped for mana, its controller adds an additional one mana of the chosen color.| +Verdant Embrace|Wilds of Eldraine Commander|136|R|{3}{G}{G}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +3/+3 and has "At the beginning of each upkeep, create a 1/1 green Saproling creature token."| +Warbriar Blessing|Wilds of Eldraine Commander|137|C|{1}{G}|Enchantment - Aura|||Enchant creature you control$When Warbriar Blessing enters the battlefield, enchanted creature fights up to one target creature you don't control.$Enchanted creature gets +0/+2.| +Glen Elendra Liege|Wilds of Eldraine Commander|138|R|{1}{U/B}{U/B}{U/B}|Creature - Faerie Knight|2|3|Flying$Other blue creatures you control get +1/+1.$Other black creatures you control get +1/+1.| +Halo Forager|Wilds of Eldraine Commander|139|U|{1}{U}{B}|Creature - Faerie Rogue|3|1|Flying$When Halo Forager enters the battlefield, you may pay {X}. When you do, you may cast target instant or sorcery card with mana value X from a graveyard without paying its mana cost. If that spell would be put into a graveyard, exile it instead.| +Jukai Naturalist|Wilds of Eldraine Commander|140|U|{G}{W}|Enchantment Creature - Human Monk|2|2|Lifelink$Enchantment spells you cast cost {1} less to cast.| +Nymris, Oona's Trickster|Wilds of Eldraine Commander|141|R|{3}{U}{B}|Legendary Creature - Faerie Knight|1|6|Flash$Flying$Whenever you cast your first spell during each opponent's turn, look at the top two cards of your library. Put one of those cards into your hand and the other into your graveyard.| +Oona, Queen of the Fae|Wilds of Eldraine Commander|142|R|{3}{U/B}{U/B}{U/B}|Legendary Creature - Faerie Wizard|5|5|Flying${X}{U/B}: Choose a color. Target opponent exiles the top X cards of their library. For each card of the chosen color exiled this way, create a 1/1 blue and black Faerie Rogue creature token with flying.| +Pollenbright Wings|Wilds of Eldraine Commander|143|U|{4}{G}{W}|Enchantment - Aura|||Enchant creature$Enchanted creature has flying.$Whenever enchanted creature deals combat damage to a player, create that many 1/1 green Saproling creature tokens.| +Siona, Captain of the Pyleas|Wilds of Eldraine Commander|144|U|{1}{G}{W}|Legendary Creature - Human Soldier|2|2|When Siona, Captain of the Pyleas enters the battlefield, look at the top seven cards of your library. You may reveal an Aura card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.$Whenever an Aura you control becomes attached to a creature you control, create a 1/1 white Human Soldier creature token.| +Arcane Signet|Wilds of Eldraine Commander|145|C|{2}|Artifact|||{T}: Add one mana of any color in your commander's color identity.| +Dimir Signet|Wilds of Eldraine Commander|146|U|{2}|Artifact|||{1}, {T}: Add {U}{B}.| +Fellwar Stone|Wilds of Eldraine Commander|147|U|{2}|Artifact|||{T}: Add one mana of any color that a land an opponent controls could produce.| +Mind Stone|Wilds of Eldraine Commander|148|C|{2}|Artifact|||{T}: Add {C}.${1}, {T}, Sacrifice Mind Stone: Draw a card.| +Sol Ring|Wilds of Eldraine Commander|149|U|{1}|Artifact|||{T}: Add {C}{C}.| +Talisman of Dominance|Wilds of Eldraine Commander|150|U|{2}|Artifact|||{T}: Add {C}.${T}: Add {U} or {B}. Talisman of Dominance deals 1 damage to you.| +Wayfarer's Bauble|Wilds of Eldraine Commander|151|C|{1}|Artifact|||{2}, {T}, Sacrifice Wayfarer's Bauble: Search your library for a basic land card, put that card onto the battlefield tapped, then shuffle.| +Bojuka Bog|Wilds of Eldraine Commander|152|C||Land|||Bojuka Bog enters the battlefield tapped.$When Bojuka Bog enters the battlefield, exile target player's graveyard.${T}: Add {B}.| +Canopy Vista|Wilds of Eldraine Commander|153|R||Land - Forest Plains|||({T}: Add {G} or {W}.)$Canopy Vista enters the battlefield tapped unless you control two or more basic lands.| +Castle Ardenvale|Wilds of Eldraine Commander|154|R||Land|||Castle Ardenvale enters the battlefield tapped unless you control a Plains.${T}: Add {W}.${2}{W}{W}, {T}: Create a 1/1 white Human creature token.| +Choked Estuary|Wilds of Eldraine Commander|155|R||Land|||As Choked Estuary enters the battlefield, you may reveal an Island or Swamp card from your hand. If you don't, Choked Estuary enters the battlefield tapped.${T}: Add {U} or {B}.| +Command Tower|Wilds of Eldraine Commander|156|C||Land|||{T}: Add one mana of any color in your commander's color identity.| +Darkwater Catacombs|Wilds of Eldraine Commander|157|R||Land|||{1}, {T}: Add {U}{B}.| +Dimir Aqueduct|Wilds of Eldraine Commander|158|U||Land|||Dimir Aqueduct enters the battlefield tapped.$When Dimir Aqueduct enters the battlefield, return a land you control to its owner's hand.${T}: Add {U}{B}.| +Exotic Orchard|Wilds of Eldraine Commander|159|R||Land|||{T}: Add one mana of any color that a land an opponent controls could produce.| +Faerie Conclave|Wilds of Eldraine Commander|160|U||Land|||Faerie Conclave enters the battlefield tapped.${T}: Add {U}.${1}{U}: Faerie Conclave becomes a 2/1 blue Faerie creature with flying until end of turn. It's still a land.| +Fortified Village|Wilds of Eldraine Commander|161|R||Land|||As Fortified Village enters the battlefield, you may reveal a Forest or Plains card from your hand. If you don't, Fortified Village enters the battlefield tapped.${T}: Add {G} or {W}.| +Hall of Heliod's Generosity|Wilds of Eldraine Commander|162|R||Legendary Land|||{T}: Add {C}.${1}{W}, {T}: Put target enchantment card from your graveyard on top of your library.| +Krosan Verge|Wilds of Eldraine Commander|163|U||Land|||Krosan Verge enters the battlefield tapped.${T}: Add {C}.${2}, {T}, Sacrifice Krosan Verge: Search your library for a Forest card and a Plains card, put them onto the battlefield tapped, then shuffle.| +Myriad Landscape|Wilds of Eldraine Commander|164|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.| +Path of Ancestry|Wilds of Eldraine Commander|165|C||Land|||Path of Ancestry enters the battlefield tapped.${T}: Add one mana of any color in your commander's color identity. When that mana is spent to cast a creature spell that shares a creature type with your commander, scry 1.| +Secluded Glen|Wilds of Eldraine Commander|166|R||Land|||As Secluded Glen enters the battlefield, you may reveal a Faerie card from your hand. If you don't, Secluded Glen enters the battlefield tapped.${T}: Add {U} or {B}.| +Sungrass Prairie|Wilds of Eldraine Commander|167|R||Land|||{1}, {T}: Add {G}{W}.| +Sunken Hollow|Wilds of Eldraine Commander|168|R||Land - Island Swamp|||({T}: Add {U} or {B}.)$Sunken Hollow enters the battlefield tapped unless you control two or more basic lands.| +Tainted Isle|Wilds of Eldraine Commander|169|U||Land|||{T}: Add {C}.${T}: Add {U} or {B}. Activate only if you control a Swamp.| +Temple of Deceit|Wilds of Eldraine Commander|170|R||Land|||Temple of Deceit enters the battlefield tapped.$When Temple of Deceit enters the battlefield, scry 1.${T}: Add {U} or {B}.| +Temple of Plenty|Wilds of Eldraine Commander|171|R||Land|||Temple of Plenty enters the battlefield tapped.$When Temple of Plenty enters the battlefield, scry 1.${T}: Add {G} or {W}.| +Temple of the False God|Wilds of Eldraine Commander|172|U||Land|||{T}: Add {C}{C}. Activate only if you control five or more lands.| +Vitu-Ghazi, the City-Tree|Wilds of Eldraine Commander|173|U||Land|||{T}: Add {C}.${2}{G}{W}, {T}: Create a 1/1 green Saproling creature token.| diff --git a/Utils/mtg-sets-data.txt b/Utils/mtg-sets-data.txt index 002616ae51c..b9b75bb0d96 100644 --- a/Utils/mtg-sets-data.txt +++ b/Utils/mtg-sets-data.txt @@ -121,6 +121,8 @@ Iconic Masters|IMA| Ikoria: Lair of Behemoths|IKO| Invasion|INV| Innistrad|ISD| +Lost Caverns of Ixalan|LCI| +Lost Caverns of Ixalan Commander|LCC| Innistrad: Midnight Hunt|MID| Midnight Hunt Commander|MIC| Innistrad: Crimson Vow|VOW| @@ -202,6 +204,7 @@ Prerelease Events|PPRE| Portal Three Kingdoms|PTK| Ravnica Allegiance|RNA| Ravnica: City of Guilds|RAV| +Jurassic World Collection|REX| Rivals of Ixalan|RIX| Rise of the Eldrazi|ROE| Return to Ravnica|RTR| @@ -217,6 +220,7 @@ Scars of Mirrodin|SOM| Streets of New Capenna|SNC| New Capenna Commander|NCC| Stronghold|STH| +Special Guests|SPG| Super Series|SUS| Theros|THS| Theros Beyond Death|THB|