From 64f9dc6e60735ab66c5058828a159b104854738b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 15 Mar 2018 23:37:05 +0100 Subject: [PATCH 01/20] * Add Land Dialog - Added deck size value and images of mana symbols. --- .../mage/client/dialog/AddLandDialog.form | 236 ++++++++++++++---- .../mage/client/dialog/AddLandDialog.java | 211 +++++++++++----- 2 files changed, 335 insertions(+), 112 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.form b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.form index 6db932f598b..53405d95e3a 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.form @@ -7,6 +7,11 @@ + + + + + @@ -29,40 +34,62 @@ - + - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + @@ -79,35 +106,54 @@ + - - - + + + + + + - - - + + + + + + - - - + + + + + + - - - + + + + + + - + + + + + + + @@ -120,7 +166,7 @@ - + @@ -130,9 +176,23 @@ + + + + + + + + + + + + + + - + @@ -142,9 +202,22 @@ + + + + + + + + + + + + + - + @@ -154,9 +227,22 @@ + + + + + + + + + + + + + - + @@ -166,9 +252,22 @@ + + + + + + + + + + + + + - + @@ -178,9 +277,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -194,14 +328,6 @@ - - - - - - - - diff --git a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java index 792c4dd9014..19c3db50f8e 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java @@ -27,12 +27,13 @@ */ package mage.client.dialog; -import java.util.HashSet; +import java.awt.image.BufferedImage; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import javax.swing.DefaultComboBoxModel; +import javax.swing.ImageIcon; import javax.swing.JLayeredPane; import mage.Mana; import mage.cards.Card; @@ -49,6 +50,7 @@ import mage.client.util.gui.FastSearchUtil; import mage.constants.Rarity; import mage.util.RandomUtil; import org.apache.log4j.Logger; +import org.mage.card.arcane.ManaSymbols; /** * @@ -59,7 +61,6 @@ public class AddLandDialog extends MageDialog { private static final Logger logger = Logger.getLogger(MageDialog.class); private Deck deck; - private final Set landSetCodes = new HashSet<>(); private static final int DEFAULT_SEALED_DECK_CARD_NUMBER = 40; @@ -131,6 +132,27 @@ public class AddLandDialog extends MageDialog { } else { MageFrame.getDesktop().add(this, JLayeredPane.PALETTE_LAYER); } + spnDeckSize.setValue(DEFAULT_SEALED_DECK_CARD_NUMBER); + BufferedImage image = ManaSymbols.getSizedManaSymbol("G", 15); + if (image != null) { + lblForestIcon.setIcon(new ImageIcon(image)); + } + image = ManaSymbols.getSizedManaSymbol("U", 15); + if (image != null) { + lblIslandIcon.setIcon(new ImageIcon(image)); + } + image = ManaSymbols.getSizedManaSymbol("W", 15); + if (image != null) { + lblPlainsIcon.setIcon(new ImageIcon(image)); + } + image = ManaSymbols.getSizedManaSymbol("R", 15); + if (image != null) { + lblMountainIcon.setIcon(new ImageIcon(image)); + } + image = ManaSymbols.getSizedManaSymbol("B", 15); + if (image != null) { + lblSwampIcon.setIcon(new ImageIcon(image)); + } this.setVisible(true); } @@ -139,9 +161,7 @@ public class AddLandDialog extends MageDialog { String landSetName = (String) cbLandSet.getSelectedItem(); CardCriteria criteria = new CardCriteria(); - if (landSetName.equals("")) { - criteria.setCodes(landSetCodes.toArray(new String[landSetCodes.size()])); - } else { + if (!landSetName.equals("")) { ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByName(landSetName); if (expansionInfo == null) { throw new IllegalArgumentException("Code of Set " + landSetName + " not found"); @@ -193,20 +213,28 @@ public class AddLandDialog extends MageDialog { private void initComponents() { jButton2 = new javax.swing.JButton(); + jLabel1 = new javax.swing.JLabel(); lblLandSet = new javax.swing.JLabel(); lblForest = new javax.swing.JLabel(); spnForest = new javax.swing.JSpinner(); + lblForestIcon = new javax.swing.JLabel(); lblIsland = new javax.swing.JLabel(); spnIsland = new javax.swing.JSpinner(); + lblIslandIcon = new javax.swing.JLabel(); lblMountain = new javax.swing.JLabel(); spnMountain = new javax.swing.JSpinner(); + lblMountainIcon = new javax.swing.JLabel(); lblPains = new javax.swing.JLabel(); spnPlains = new javax.swing.JSpinner(); + lblPlainsIcon = new javax.swing.JLabel(); lblSwamp = new javax.swing.JLabel(); spnSwamp = new javax.swing.JSpinner(); + lblSwampIcon = new javax.swing.JLabel(); + lblDeckSize = new javax.swing.JLabel(); + spnDeckSize = new javax.swing.JSpinner(); + btnAutoAdd = new javax.swing.JButton(); btnAdd = new javax.swing.JButton(); btnCancel = new javax.swing.JButton(); - btnAutoAdd = new javax.swing.JButton(); panelSet = new javax.swing.JPanel(); cbLandSet = new javax.swing.JComboBox(); btnSetFastSearch = new javax.swing.JButton(); @@ -214,31 +242,67 @@ public class AddLandDialog extends MageDialog { jButton2.setText("jButton2"); + jLabel1.setText("jLabel1"); + setTitle("Add Land"); lblLandSet.setText("Set:"); - lblForest.setText("Forest"); + lblForest.setText("Forest:"); spnForest.setModel(new javax.swing.SpinnerNumberModel(0, 0, null, 1)); - lblIsland.setText("Island"); + lblForestIcon.setToolTipText(""); + lblForestIcon.setMaximumSize(new java.awt.Dimension(22, 20)); + lblForestIcon.setMinimumSize(new java.awt.Dimension(22, 20)); + lblForestIcon.setPreferredSize(new java.awt.Dimension(22, 20)); + + lblIsland.setText("Island:"); spnIsland.setModel(new javax.swing.SpinnerNumberModel(0, 0, null, 1)); - lblMountain.setText("Mountain"); + lblIslandIcon.setMaximumSize(new java.awt.Dimension(22, 20)); + lblIslandIcon.setMinimumSize(new java.awt.Dimension(22, 20)); + lblIslandIcon.setPreferredSize(new java.awt.Dimension(22, 20)); + + lblMountain.setText("Mountain:"); spnMountain.setModel(new javax.swing.SpinnerNumberModel(0, 0, null, 1)); - lblPains.setText("Plains"); + lblMountainIcon.setMaximumSize(new java.awt.Dimension(22, 20)); + lblMountainIcon.setMinimumSize(new java.awt.Dimension(22, 20)); + lblMountainIcon.setPreferredSize(new java.awt.Dimension(22, 20)); + + lblPains.setText("Plains:"); spnPlains.setModel(new javax.swing.SpinnerNumberModel(0, 0, null, 1)); - lblSwamp.setText("Swamp"); + lblPlainsIcon.setMaximumSize(new java.awt.Dimension(22, 20)); + lblPlainsIcon.setMinimumSize(new java.awt.Dimension(22, 20)); + lblPlainsIcon.setPreferredSize(new java.awt.Dimension(22, 20)); + + lblSwamp.setText("Swamp:"); spnSwamp.setModel(new javax.swing.SpinnerNumberModel(0, 0, null, 1)); + lblSwampIcon.setMaximumSize(new java.awt.Dimension(22, 20)); + lblSwampIcon.setMinimumSize(new java.awt.Dimension(22, 20)); + lblSwampIcon.setPreferredSize(new java.awt.Dimension(22, 20)); + + lblDeckSize.setText("Deck size:"); + + spnDeckSize.setModel(new javax.swing.SpinnerNumberModel(0, 0, null, 1)); + + btnAutoAdd.setText("Suggest"); + btnAutoAdd.setToolTipText("Propose related to the mana costs of the cards in the deck
\nthe number of lands to add to get to the set deck size."); + btnAutoAdd.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnAutoAddActionPerformed(evt); + } + }); + btnAdd.setText("Add"); + btnAdd.setToolTipText("Add the selected number of basic lands to the deck."); btnAdd.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnAddActionPerformed(evt); @@ -252,13 +316,6 @@ public class AddLandDialog extends MageDialog { } }); - btnAutoAdd.setText("Suggest"); - btnAutoAdd.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - btnAutoAddActionPerformed(evt); - } - }); - panelSet.setLayout(new javax.swing.BoxLayout(panelSet, javax.swing.BoxLayout.LINE_AXIS)); cbLandSet.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); @@ -285,32 +342,50 @@ public class AddLandDialog extends MageDialog { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lblMountain) - .addComponent(lblForest, javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lblLandSet, javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lblIsland, javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lblPains, javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lblSwamp, javax.swing.GroupLayout.Alignment.TRAILING)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lblMountain) + .addComponent(lblForest, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lblLandSet, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lblIsland, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lblPains, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lblSwamp, javax.swing.GroupLayout.Alignment.TRAILING)) + .addComponent(lblDeckSize)) .addGap(18, 18, 18) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(ckbFullArtLands) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(btnAdd) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnCancel) + .addContainerGap()) + .addComponent(ckbFullArtLands) + .addComponent(panelSet, javax.swing.GroupLayout.PREFERRED_SIZE, 219, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(spnForest, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblForestIcon, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(spnIsland, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblIslandIcon, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(spnMountain, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblMountainIcon, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(spnSwamp, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblSwampIcon, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(spnDeckSize, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(btnAutoAdd))) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(btnAutoAdd, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(spnMountain, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) - .addComponent(spnIsland, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) - .addComponent(spnForest, javax.swing.GroupLayout.Alignment.LEADING)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(spnSwamp, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) - .addComponent(spnPlains, javax.swing.GroupLayout.Alignment.LEADING)))) + .addComponent(spnPlains, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnAdd) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnCancel)) - .addComponent(panelSet, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addComponent(lblPlainsIcon, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(36, 36, 36)))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -322,30 +397,44 @@ public class AddLandDialog extends MageDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(lblForest) - .addComponent(spnForest, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(spnForest, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lblForestIcon, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblIsland) - .addComponent(spnIsland, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblIsland) + .addComponent(spnIsland, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(lblIslandIcon, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblMountain) - .addComponent(spnMountain, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblMountain) + .addComponent(spnMountain, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(lblMountainIcon, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblPains) - .addComponent(spnPlains, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblPains) + .addComponent(spnPlains, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(lblPlainsIcon, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblSwamp) + .addComponent(spnSwamp, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(lblSwampIcon, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblSwamp) - .addComponent(spnSwamp, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(ckbFullArtLands) .addGap(2, 2, 2) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(btnAutoAdd) + .addComponent(lblDeckSize) + .addComponent(spnDeckSize, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(btnAdd) - .addComponent(btnCancel))) + .addComponent(btnCancel)) + .addContainerGap()) ); pack(); @@ -386,7 +475,7 @@ public class AddLandDialog extends MageDialog { int blue = 0; int white = 0; Set cards = deck.getCards(); - int land_number = DEFAULT_SEALED_DECK_CARD_NUMBER - cards.size(); + int land_number = ((Number) spnDeckSize.getValue()).intValue() - cards.size(); if (land_number < 0) { land_number = 0; } @@ -427,13 +516,21 @@ public class AddLandDialog extends MageDialog { private javax.swing.JComboBox cbLandSet; private javax.swing.JCheckBox ckbFullArtLands; private javax.swing.JButton jButton2; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel lblDeckSize; private javax.swing.JLabel lblForest; + private javax.swing.JLabel lblForestIcon; private javax.swing.JLabel lblIsland; + private javax.swing.JLabel lblIslandIcon; private javax.swing.JLabel lblLandSet; private javax.swing.JLabel lblMountain; + private javax.swing.JLabel lblMountainIcon; private javax.swing.JLabel lblPains; + private javax.swing.JLabel lblPlainsIcon; private javax.swing.JLabel lblSwamp; + private javax.swing.JLabel lblSwampIcon; private javax.swing.JPanel panelSet; + private javax.swing.JSpinner spnDeckSize; private javax.swing.JSpinner spnForest; private javax.swing.JSpinner spnIsland; private javax.swing.JSpinner spnMountain; From 753e039d02f3cbe4168d0136af5b10fa7e5ce91b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 16 Mar 2018 00:28:12 +0100 Subject: [PATCH 02/20] * Fixed a problem that a tooltip window was shown of cards from panels in the back. --- Mage.Client/src/main/java/mage/client/MageFrame.java | 2 +- .../client/plugins/adapters/MageActionCallback.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 77d7ebf6ef4..10ad84de4a4 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -613,7 +613,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { } } - private static MagePane getTopMost(MagePane exclude) { + public static MagePane getTopMost(MagePane exclude) { MagePane topmost = null; int best = Integer.MAX_VALUE; for (Component frame : desktopPane.getComponentsInLayer(JLayeredPane.DEFAULT_LAYER)) { diff --git a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java index 8c6e8538596..d428ad1f658 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java +++ b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java @@ -26,10 +26,12 @@ import mage.cards.MageCard; import mage.cards.action.ActionCallback; import mage.cards.action.TransferData; import mage.client.MageFrame; +import mage.client.MagePane; import mage.client.SessionHandler; import mage.client.cards.BigCard; import mage.client.components.MageComponents; import mage.client.dialog.PreferencesDialog; +import mage.client.game.GamePane; import mage.client.plugins.impl.Plugins; import mage.client.util.DefaultActionCallback; import mage.client.util.gui.ArrowBuilder; @@ -367,6 +369,16 @@ public class MageActionCallback implements ActionCallback { } private void handleOverNewView(TransferData data) { + // Prevent to show tooltips from panes not in front + MagePane topPane = MageFrame.getTopMost(null); + if (topPane instanceof GamePane) { + if (!((GamePane) topPane).getGameId().equals(data.gameId)) { + return; + } + } else if (data.gameId != null) { + return; + } + hideTooltipPopup(); cancelTimeout(); Component parentComponent = SwingUtilities.getRoot(data.component); From 89d58bef5b4007824be3775eb12478a15cfb0748 Mon Sep 17 00:00:00 2001 From: spjspj Date: Fri, 16 Mar 2018 22:49:21 +1100 Subject: [PATCH 03/20] Add rendering for Expeditions (With non-copyright background image) --- .../org/mage/card/arcane/ManaSymbols.java | 10 +- .../mage/card/arcane/ModernCardRenderer.java | 108 +++++++++++++----- .../card/arcane/ModernSplitCardRenderer.java | 2 +- .../background_texture_expedition.png | Bin 0 -> 39641 bytes .../src/mage/sets/ZendikarExpeditions.java | 90 +++++++-------- 5 files changed, 133 insertions(+), 77 deletions(-) create mode 100644 Mage.Client/src/main/resources/cardrender/background_texture_expedition.png diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java index a6903e42a1f..b64b81bf0ac 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java @@ -548,8 +548,8 @@ public final class ManaSymbols { public static void draw(Graphics g, String manaCost, int x, int y, int symbolWidth, Color symbolsTextColor, int symbolMarginX) { if (!manaImages.containsKey(symbolWidth)) { loadSymbolImages(symbolWidth); - } - + } + // TODO: replace with jlabel render (look at table rendere)? /* @@ -602,12 +602,16 @@ public final class ManaSymbols { return; } - manaCost = manaCost.replace("\\", ""); + manaCost = manaCost.replace("\\", ""); manaCost = UI.getDisplayManaCost(manaCost); StringTokenizer tok = new StringTokenizer(manaCost, " "); while (tok.hasMoreTokens()) { String symbol = tok.nextToken(); Image image = sizedSymbols.get(symbol); + if (image == null && symbol != null) { + String symbol2 = "" + symbol.charAt(1) + symbol.charAt(0); + image = sizedSymbols.get(symbol2); + } if (image == null) { // TEXT draw diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index 67e5192061d..20eb2520863 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -34,6 +34,7 @@ import mage.util.SubTypeList; import mage.view.CardView; import mage.view.PermanentView; import org.apache.log4j.Logger; +import static org.mage.card.arcane.ManaSymbols.getSizedManaSymbol; /* @@ -72,13 +73,13 @@ public class ModernCardRenderer extends CardRenderer { BufferedImage img = CardRendererUtils.toBufferedImage(icon.getImage()); return new TexturePaint(img, new Rectangle(0, 0, img.getWidth(), img.getHeight())); } - + private static BufferedImage loadBackgroundImage(String name) { URL url = ModernCardRenderer.class.getResource("/cardrender/background_texture_" + name + ".png"); ImageIcon icon = new ImageIcon(url); BufferedImage img = CardRendererUtils.toBufferedImage(icon.getImage()); return img; - } + } private static BufferedImage loadFramePart(String name) { URL url = ModernCardRenderer.class.getResource("/cardrender/" + name + ".png"); @@ -108,7 +109,7 @@ public class ModernCardRenderer extends CardRenderer { public static final Paint BG_TEXTURE_ARTIFACT = loadBackgroundTexture("artifact"); public static final Paint BG_TEXTURE_LAND = loadBackgroundTexture("land"); public static final Paint BG_TEXTURE_VEHICLE = loadBackgroundTexture("vehicle"); - + public static final BufferedImage BG_IMG_WHITE = loadBackgroundImage("white"); public static final BufferedImage BG_IMG_BLUE = loadBackgroundImage("blue"); public static final BufferedImage BG_IMG_BLACK = loadBackgroundImage("black"); @@ -119,7 +120,8 @@ public class ModernCardRenderer extends CardRenderer { public static final BufferedImage BG_IMG_LAND = loadBackgroundImage("land"); public static final BufferedImage BG_IMG_VEHICLE = loadBackgroundImage("vehicle"); public static final BufferedImage BG_IMG_COLORLESS = loadBackgroundImage("colorless"); - + public static final BufferedImage BG_IMG_EXPEDITION = loadBackgroundImage("expedition"); + public static final BufferedImage FRAME_INVENTION = loadFramePart("invention_frame"); public static final Color BORDER_WHITE = new Color(216, 203, 188); @@ -301,7 +303,11 @@ public class ModernCardRenderer extends CardRenderer { // Just draw a brown rectangle drawCardBack(g); } else { - BufferedImage bg = getBackgroundImage(cardView.getColor(), cardView.getCardTypes(), cardView.getSubTypes()); + boolean isExped = false; + if (cardView.getExpansionSetCode().equals("EXP")) { + isExped = true; + } + BufferedImage bg = getBackgroundImage(cardView.getColor(), cardView.getCardTypes(), cardView.getSubTypes(), isExped); if (bg == null) { return; } @@ -318,12 +324,12 @@ public class ModernCardRenderer extends CardRenderer { cardWidth - borderWidth * 2, cornerRadius * 4, cornerRadius * 2, cornerRadius * 2); a.add(new Area(rr2)); - + // Draw the M15 rounded "swoosh" at the bottom Rectangle r = new Rectangle(borderWidth + contentInset, cardHeight - borderWidth * 5, cardWidth - borderWidth * 2 - contentInset * 2, borderWidth * 2); a.add(new Area(r)); g.setClip(a); - g.drawImage(bg, 0, 0, cardWidth, cardHeight, 0, 0, bgw, bgh, BOX_BLUE, null); + g.drawImage(bg, 0, 0, cardWidth, cardHeight, 0, 0, bgw, bgh, BOX_BLUE, null); g.setClip(null); } } @@ -545,24 +551,13 @@ public class ModernCardRenderer extends CardRenderer { if (!isZendikarFullArtLand()) { drawRulesText(g, textboxKeywords, textboxRules, totalContentInset + 2, typeLineY + boxHeight + 2, - contentWidth - 4, cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3); + contentWidth - 4, cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3, false); } else { int x = totalContentInset; int y = typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset; int w = contentWidth; int h = boxHeight - 4; - CardRendererUtils.drawZendikarLandBox(g, - x, y, w, h, - contentInset, - borderPaint, boxColor); - drawTypeLine(g, getCardSuperTypeLine(), - totalContentInset + contentInset, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, - contentWidth / 2 - boxHeight, boxHeight - 4, false); - drawTypeLine(g, getCardSubTypeLine(), - totalContentInset + 4 * contentWidth / 7 + boxHeight, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, - 3 * contentWidth / 7 - boxHeight - contentInset, boxHeight - 4, true); - if (cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) { // Draw curved lines (old Zendikar land style) - bigger (around 6%) inset on curve on bottom than inset (around 4.5%) on top... int x2 = x + contentWidth; @@ -584,9 +579,49 @@ public class ModernCardRenderer extends CardRenderer { boxColor, borderPaint); } + // If an expedition, needs the rules box to be visible. + if (cardView.getExpansionSetCode().equals("EXP")) { + // Draw a small separator between the type line and box, and shadow + // at the left of the texbox, and above the name line + g.setPaint(textboxPaint); + float alpha = 0.55f; + AlphaComposite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha); + Composite origc = g.getComposite(); + g.setComposite(comp); + g.setBackground(new Color(155, 0, 0, 150)); + + g.fillRect( + totalContentInset + 1, typeLineY - boxHeight, + contentWidth - 2, cardHeight - borderWidth * 3 - typeLineY - 1); + + g.setComposite(origc); + + g.fillRect( + totalContentInset - 1, totalContentInset - 1, + contentWidth + 1, 1); + + g.fillRect( + totalContentInset + 1, typeLineY - boxHeight, + contentWidth - 2, 1); + + drawRulesText(g, textboxKeywords, textboxRules, + totalContentInset + 2, typeLineY - boxHeight, + contentWidth - 4, cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3, true); + } + + CardRendererUtils.drawZendikarLandBox(g, + x, y, w, h, + contentInset, + borderPaint, boxColor); + drawTypeLine(g, getCardSuperTypeLine(), + totalContentInset + contentInset, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, + contentWidth / 2 - boxHeight, boxHeight - 4, false); + drawTypeLine(g, getCardSubTypeLine(), + totalContentInset + 4 * contentWidth / 7 + boxHeight, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset, + 3 * contentWidth / 7 - boxHeight - contentInset, boxHeight - 4, true); drawRulesText(g, textboxKeywords, textboxRules, x, y, - w, h); + w, h, false); } // Draw the bottom right stuff @@ -962,7 +997,7 @@ public class ModernCardRenderer extends CardRenderer { return layout; } - protected void drawRulesText(Graphics2D g, ArrayList keywords, ArrayList rules, int x, int y, int w, int h) { + protected void drawRulesText(Graphics2D g, ArrayList keywords, ArrayList rules, int x, int y, int w, int h, boolean forceRules) { // Gather all rules to render List allRules = new ArrayList<>(rules); @@ -974,14 +1009,19 @@ public class ModernCardRenderer extends CardRenderer { } // Basic mana draw mana symbol in textbox (for basic lands) - if (allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand() || isZendikarFullArtLand()) { + if (!forceRules && (allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand() || isZendikarFullArtLand())) { if (!isZendikarFullArtLand()) { drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); return; } else // Big circle in the middle for Zendikar lands - if (allRules.size() == 1) { + { + if (allRules.size() == 1) { // Size of mana symbol = 9/4 * h, 3/4h above line - drawBasicManaSymbol(g, x + w / 2 - 9 * h / 8 + 1, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); + if (allRules.get(0) instanceof TextboxBasicManaRule) { + drawBasicManaSymbol(g, x + w / 2 - 9 * h / 8 + 1, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol()); + } else { + drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, cardView.getFrameColor().toString()); + } return; } else { if (allRules.size() > 1) { @@ -989,6 +1029,7 @@ public class ModernCardRenderer extends CardRenderer { } return; } + } } // Go through possible font sizes in descending order to find the best fit @@ -1043,7 +1084,15 @@ public class ModernCardRenderer extends CardRenderer { private void drawBasicManaSymbol(Graphics2D g, int x, int y, int w, int h, String symbol) { String symbs = symbol; - ManaSymbols.draw(g, symbs, x, y, w, Color.black, 2); + if (getSizedManaSymbol(symbol) != null) { + ManaSymbols.draw(g, symbs, x, y, w, Color.black, 2); + } + if (symbol.length() == 2) { + String symbs2 = "" + symbol.charAt(1) + symbol.charAt(0); + if (getSizedManaSymbol(symbs2) != null) { + ManaSymbols.draw(g, symbs2, x, y, w, Color.black, 2); + } + } } // Get the first line of the textbox, the keyword string @@ -1272,13 +1321,16 @@ public class ModernCardRenderer extends CardRenderer { return new Color(71, 86, 101); } } - + // Determine which background image to use from a set of colors // and the current card. - protected static BufferedImage getBackgroundImage(ObjectColor colors, Collection types, SubTypeList subTypes) { + protected static BufferedImage getBackgroundImage(ObjectColor colors, Collection types, SubTypeList subTypes, boolean isExped) { if (subTypes.contains(SubType.VEHICLE)) { return BG_IMG_VEHICLE; } else if (types.contains(CardType.LAND)) { + if (isExped) { + return BG_IMG_EXPEDITION; + } return BG_IMG_LAND; } else if (types.contains(CardType.ARTIFACT)) { return BG_IMG_ARTIFACT; @@ -1299,7 +1351,7 @@ public class ModernCardRenderer extends CardRenderer { return BG_IMG_COLORLESS; } } - + // Get the box color for the given colors protected Color getBoxColor(ObjectColor colors, Collection types, boolean isNightCard) { if (cardView.isAbility()) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java index 0310d8e3acc..7e09c8a266c 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernSplitCardRenderer.java @@ -272,7 +272,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer { // Draw the textbox rules drawRulesText(g, half.keywords, half.rules, 2, typeLineY + boxHeight + 2 - 4, - half.cw - 4, half.ch - typeLineY - boxHeight); + half.cw - 4, half.ch - typeLineY - boxHeight, false); } private Graphics2D getUnmodifiedHalfContext(Graphics2D g) { diff --git a/Mage.Client/src/main/resources/cardrender/background_texture_expedition.png b/Mage.Client/src/main/resources/cardrender/background_texture_expedition.png new file mode 100644 index 0000000000000000000000000000000000000000..9ccb96d775071ce61e0b87171fcfb7e8f4ebc538 GIT binary patch literal 39641 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 Date: Fri, 16 Mar 2018 13:13:16 +0100 Subject: [PATCH 04/20] * It That Betrays - Fixed that target handling of second triggered ability did not work correct (fixes #4568). --- ...icesNonTokenPermanentTriggeredAbility.java | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/common/OpponentSacrificesNonTokenPermanentTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/OpponentSacrificesNonTokenPermanentTriggeredAbility.java index 80388d591e8..ee4b9e169ba 100644 --- a/Mage/src/main/java/mage/abilities/common/OpponentSacrificesNonTokenPermanentTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/OpponentSacrificesNonTokenPermanentTriggeredAbility.java @@ -1,16 +1,16 @@ /* * Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. @@ -53,12 +53,10 @@ public class OpponentSacrificesNonTokenPermanentTriggeredAbility extends Trigger @Override public boolean checkTrigger(GameEvent event, Game game) { - if (game.getPlayer(getControllerId()).hasOpponent(event.getPlayerId(), game)) { + if (game.getPlayer(getControllerId()).hasOpponent(event.getPlayerId(), game)) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); - if (permanent != null && !(permanent instanceof PermanentToken) ) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - } + if (permanent != null && !(permanent instanceof PermanentToken)) { + getEffects().setTargetPointer(new FixedTarget(permanent, game)); return true; } } @@ -75,4 +73,4 @@ public class OpponentSacrificesNonTokenPermanentTriggeredAbility extends Trigger return new OpponentSacrificesNonTokenPermanentTriggeredAbility(this); } -} \ No newline at end of file +} From 8b8b3752e87096fb8dd57f913ea0289f2d0a285d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 16 Mar 2018 13:13:25 +0100 Subject: [PATCH 05/20] * It That Betrays - Fixed that target handling of second triggered ability did not work correct (fixes #4568). --- .../abilities/keywords/AnnihilatorTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AnnihilatorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AnnihilatorTest.java index 79e622d52d0..7c84ee87c5e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AnnihilatorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/AnnihilatorTest.java @@ -31,5 +31,52 @@ public class AnnihilatorTest extends CardTestPlayerBase { assertPermanentCount(playerA, 1); } + /** + * I was attacked with an It that Betrays while i had an Academy Rector and + * with the annihilator trigger on the stack i cast Cauldron Haze targeting + * academy rector then sacrificed her to the annihilator trigger and chose + * not to exile her. My persist resolved before the second ability of it + * that betrays because i was not the active player, the game log shows: + * + * 9:18 AM: Ability triggers: Academy Rector [e15] - Persist (When this + * creature dies, if it had no -1/-1 counters on it, return it to the + * battlefield under its owner's control with a -1/-1 counter on it.) + * + * 9:19 AM: EllNubNub puts Academy Rector [e15] from graveyard onto the + * Battlefield + * + * 9:20 AM: hellmo puts Academy Rector [e15] from battlefield onto the + * Battlefield + * + * The It that Betrays trigger should have fissled, instead it stole her + * from my battlefield and removed the persist counter. + */ + @Test + public void testCardItThatBetrays() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + // Choose any number of target creatures. Each of those creatures gains persist until end of turn. + // Persist (When this creature dies, if it had no -1/-1 counters on it, return it to the battlefield under its owner's control with a -1/-1 counter on it.) + addCard(Zone.HAND, playerA, "Cauldron Haze", 1); // Instant {1}{W/B} + + // When Academy Rector dies, you may exile it. If you do, search your library for an enchantment card, put that card onto the battlefield, then shuffle your library. + addCard(Zone.BATTLEFIELD, playerA, "Academy Rector", 1); + + // Annihilator 2 (Whenever this creature attacks, defending player sacrifices two permanents.) + // Whenever an opponent sacrifices a nontoken permanent, put that card onto the battlefield under your control. + addCard(Zone.BATTLEFIELD, playerB, "It That Betrays"); + + attack(2, playerB, "It That Betrays"); + setChoice(playerA, "Academy Rector"); // Annihilator + setChoice(playerA, "Plains"); // Annihilator + castSpell(2, PhaseStep.DECLARE_ATTACKERS, playerA, "Cauldron Haze", "Academy Rector", "Annihilator"); + setChoice(playerA, "No"); // Academy Rector No Exile + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Cauldron Haze", 1); + assertPermanentCount(playerB, "Academy Rector", 0); + assertPowerToughness(playerA, "Academy Rector", 0, 1); + } } From fdd8cd0e092dc0e20315bdeb434b8764a9f9d753 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 16 Mar 2018 15:04:31 +0100 Subject: [PATCH 06/20] * It That Betrays - Fixed that target handling of second triggered ability did not work correct (fixes #4568). --- .../OpponentSacrificesNonTokenPermanentTriggeredAbility.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/abilities/common/OpponentSacrificesNonTokenPermanentTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/OpponentSacrificesNonTokenPermanentTriggeredAbility.java index ee4b9e169ba..271cc11bd19 100644 --- a/Mage/src/main/java/mage/abilities/common/OpponentSacrificesNonTokenPermanentTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/OpponentSacrificesNonTokenPermanentTriggeredAbility.java @@ -56,7 +56,7 @@ public class OpponentSacrificesNonTokenPermanentTriggeredAbility extends Trigger if (game.getPlayer(getControllerId()).hasOpponent(event.getPlayerId(), game)) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (permanent != null && !(permanent instanceof PermanentToken)) { - getEffects().setTargetPointer(new FixedTarget(permanent, game)); + getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game.getState().getZoneChangeCounter(event.getTargetId()))); return true; } } From b035d85f983ddfd9e39ead3ff084e4c5dd6fd21f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 16 Mar 2018 15:06:15 +0100 Subject: [PATCH 07/20] * Fixed a problem that triggered abilities that face down permanents got from other sourced did not trigger (fixes #4571). --- .../src/mage/cards/e/EndlessWhispers.java | 2 +- .../cards/abilities/keywords/MorphTest.java | 41 +++++++++++++++++++ .../mage/abilities/TriggeredAbilities.java | 31 ++++++++------ 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/Mage.Sets/src/mage/cards/e/EndlessWhispers.java b/Mage.Sets/src/mage/cards/e/EndlessWhispers.java index 36e78b8071d..c7976f063ea 100644 --- a/Mage.Sets/src/mage/cards/e/EndlessWhispers.java +++ b/Mage.Sets/src/mage/cards/e/EndlessWhispers.java @@ -56,7 +56,7 @@ import mage.target.common.TargetOpponent; public class EndlessWhispers extends CardImpl { public EndlessWhispers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}"); // Each creature has "When this creature dies, choose target opponent. That player puts this card from its owner's graveyard onto the battlefield under his or her control at the beginning of the next end step." DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnSourceToBattlefieldEffect()); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java index 8c8733d57dc..045c436d8e4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java @@ -853,4 +853,45 @@ public class MorphTest extends CardTestPlayerBase { assertTappedCount("Island", true, 3); } + + /** + * If you have Endless Whispers in play and a morph creature dies, it should + * be returned to play face up at end of turn under the control of an + * opponent. + */ + @Test + public void testMorphEndlessWhispers() { + /* + Quicksilver Dragon {4}{U}{U} + Creature - Dragon + 5/5 + Flying + {U}: If target spell has only one target and that target is Quicksilver Dragon, change that spell's target to another creature. + Morph {4}{U} + */ + addCard(Zone.HAND, playerA, "Quicksilver Dragon"); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + + // Each creature has "When this creature dies, choose target opponent. That player puts this card from its owner's graveyard + // onto the battlefield under his or her control at the beginning of the next end step." + addCard(Zone.BATTLEFIELD, playerA, "Endless Whispers", 1); + + addCard(Zone.HAND, playerB, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Quicksilver Dragon"); + setChoice(playerA, "Yes"); // cast it face down as 2/2 creature + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", ""); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertGraveyardCount(playerB, "Lightning Bolt", 1); + assertGraveyardCount(playerA, "Quicksilver Dragon", 0); + + assertPermanentCount(playerA, "Quicksilver Dragon", 0); + assertPermanentCount(playerB, "Quicksilver Dragon", 1); + + } } diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java index 5feca7f7afb..c04367f5576 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java @@ -28,6 +28,8 @@ */ package mage.abilities; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import mage.MageObject; import mage.constants.Zone; import mage.game.Game; @@ -36,15 +38,12 @@ import mage.game.events.NumberOfTriggersEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - /** * @author BetaSteward_at_googlemail.com - *

- * This class uses ConcurrentHashMap to avoid ConcurrentModificationExceptions. - * See ticket https://github.com/magefree/mage/issues/966 and - * https://github.com/magefree/mage/issues/473 + *

+ * This class uses ConcurrentHashMap to avoid ConcurrentModificationExceptions. + * See ticket https://github.com/magefree/mage/issues/966 and + * https://github.com/magefree/mage/issues/473 */ public class TriggeredAbilities extends ConcurrentHashMap { @@ -63,7 +62,7 @@ public class TriggeredAbilities extends ConcurrentHashMap it = this.values().iterator(); it.hasNext(); ) { + for (Iterator it = this.values().iterator(); it.hasNext();) { TriggeredAbility ability = it.next(); if (ability instanceof StateTriggeredAbility && ((StateTriggeredAbility) ability).canTrigger(game)) { checkTrigger(ability, null, game); @@ -72,7 +71,7 @@ public class TriggeredAbilities extends ConcurrentHashMap it = this.values().iterator(); it.hasNext(); ) { + for (Iterator it = this.values().iterator(); it.hasNext();) { TriggeredAbility ability = it.next(); if (ability.checkEventType(event, game)) { checkTrigger(ability, event, game); @@ -98,7 +97,10 @@ public class TriggeredAbilities extends ConcurrentHashMap so it should work + && !ability.getWorksFaceDown()) { // the ability is declared to work also face down + // Not all triggered abilities of face down creatures work if they are faced down return; } controllerSet = true; @@ -130,8 +132,8 @@ public class TriggeredAbilities extends ConcurrentHashMap key.endsWith(sourceId.toString())); } @@ -171,6 +172,10 @@ public class TriggeredAbilities extends ConcurrentHashMap Date: Fri, 16 Mar 2018 18:24:29 +0100 Subject: [PATCH 08/20] * Cultivate - Fixed possible null pointer exception. --- Mage.Sets/src/mage/cards/c/Cultivate.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/Cultivate.java b/Mage.Sets/src/mage/cards/c/Cultivate.java index 0fd4bc30e58..2959af31e44 100644 --- a/Mage.Sets/src/mage/cards/c/Cultivate.java +++ b/Mage.Sets/src/mage/cards/c/Cultivate.java @@ -27,6 +27,7 @@ */ package mage.cards.c; +import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -41,8 +42,6 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; -import java.util.UUID; - /** * * @author BetaSteward_at_googlemail.com @@ -50,7 +49,7 @@ import java.util.UUID; public class Cultivate extends CardImpl { public Cultivate(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); // Search your library for up to two basic land cards, reveal those cards, and put one onto the battlefield tapped and the other into your hand. Then shuffle your library. this.getSpellAbility().addEffect(new CultivateEffect()); @@ -96,11 +95,7 @@ class CultivateEffect extends OneShotEffect { TargetCardInLibrary target = new TargetCardInLibrary(0, 2, StaticFilters.FILTER_BASIC_LAND_CARD); if (controller.searchLibrary(target, game)) { if (!target.getTargets().isEmpty()) { - Cards revealed = new CardsImpl(); - for (UUID cardId : target.getTargets()) { - Card card = controller.getLibrary().getCard(cardId, game); - revealed.add(card); - } + Cards revealed = new CardsImpl(target.getTargets()); controller.revealCards(sourceObject.getIdName(), revealed, game); if (target.getTargets().size() == 2) { TargetCard target2 = new TargetCard(Zone.LIBRARY, filter); @@ -120,13 +115,10 @@ class CultivateEffect extends OneShotEffect { controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); } } - } - controller.shuffleLibrary(source, game); - return true; } controller.shuffleLibrary(source, game); - return false; + return true; } From 5925bdf2599f47ac2e3ec67d18ce53a92fea6c58 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 16 Mar 2018 18:25:28 +0100 Subject: [PATCH 09/20] * Fixed a problem that Spell objects were not handled always correctly in game view. --- .../src/main/java/mage/view/GameView.java | 36 +++++-------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/Mage.Common/src/main/java/mage/view/GameView.java b/Mage.Common/src/main/java/mage/view/GameView.java index b354503c567..6dc733112f0 100644 --- a/Mage.Common/src/main/java/mage/view/GameView.java +++ b/Mage.Common/src/main/java/mage/view/GameView.java @@ -27,18 +27,14 @@ */ package mage.view; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import java.io.Serializable; -import java.io.BufferedWriter; -import java.io.PrintWriter; -import java.io.FileWriter; -import java.io.IOException; - import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; - import mage.MageObject; import mage.abilities.costs.Cost; import mage.cards.Card; @@ -60,8 +56,6 @@ import mage.game.stack.StackObject; import mage.players.Player; import mage.watchers.common.CastSpellLastTurnWatcher; import org.apache.log4j.Logger; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; /** * @@ -105,7 +99,12 @@ public class GameView implements Serializable { } } for (StackObject stackObject : state.getStack()) { - if (stackObject instanceof StackAbility) { + if (stackObject instanceof Spell) { + // Spell + CardView spellView = new CardView((Spell) stackObject, game, stackObject.getControllerId().equals(createdForPlayerId)); + spellView.paid = ((Spell) stackObject).getSpellAbility().getManaCostsToPay().isPaid(); + stack.put(stackObject.getId(), spellView); + } else if (stackObject instanceof StackAbility) { // Stack Ability MageObject object = game.getObject(stackObject.getSourceId()); Card card = game.getCard(stackObject.getSourceId()); @@ -161,9 +160,7 @@ public class GameView implements Serializable { LOGGER.debug("Stack Object for stack ability not found: " + stackObject.getStackAbility().getRule()); } } else { - // Spell - stack.put(stackObject.getId(), new CardView((Spell) stackObject, game, stackObject.getControllerId().equals(createdForPlayerId))); - checkPaid(stackObject.getId(), (Spell) stackObject); + LOGGER.fatal("Unknown type of StackObject: " + stackObject.getName() + ' ' + stackObject.toString() + ' ' + stackObject.getClass().toString()); } //stackOrder.add(stackObject.getId()); } @@ -223,21 +220,6 @@ public class GameView implements Serializable { cardView.paid = true; } - private void checkPaid(UUID uuid, Spell spell) { - for (Cost cost : spell.getSpellAbility().getManaCostsToPay()) { - if (!cost.isPaid()) { - return; - } - } - CardView cardView = stack.get(uuid); - cardView.paid = true; - } - - private void setPaid(UUID uuid) { - CardView cardView = stack.get(uuid); - cardView.paid = true; - } - private void updateLatestCardView(Game game, Card card, UUID stackId) { if (!card.isTransformable()) { return; From d982e215d63fea1f3730cfac83be945423e7bafc Mon Sep 17 00:00:00 2001 From: igoudt Date: Fri, 16 Mar 2018 18:47:20 +0100 Subject: [PATCH 10/20] added shape stealer unittest --- .../java/org/mage/test/cards/triggers/ShapeStealerTest.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/ShapeStealerTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ShapeStealerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ShapeStealerTest.java new file mode 100644 index 00000000000..5eff972a8a4 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ShapeStealerTest.java @@ -0,0 +1,4 @@ +package org.mage.test.cards.triggers; + +public class ShapeStealerTest { +} From 1405bc87aecefbc30794ac2ca2205b011974bcc1 Mon Sep 17 00:00:00 2001 From: igoudt Date: Fri, 16 Mar 2018 18:49:25 +0100 Subject: [PATCH 11/20] unit test shapestealer #2 --- .../test/cards/triggers/ShapeStealerTest.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ShapeStealerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ShapeStealerTest.java index 5eff972a8a4..5908aed99fc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ShapeStealerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ShapeStealerTest.java @@ -1,4 +1,25 @@ package org.mage.test.cards.triggers; -public class ShapeStealerTest { +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class ShapeStealerTest extends CardTestPlayerBase { + + private String shapeStealer = "Shape Stealer"; + private String myojinOfCleansingFire = "Myojin of Cleansing Fire"; + + @Test + public void testShapeStealerSingleBlocker() { + addCard(Zone.BATTLEFIELD, playerA, shapeStealer); + addCard(Zone.BATTLEFIELD, playerB, myojinOfCleansingFire); + attack(1, playerA, shapeStealer); + block(1, playerB, myojinOfCleansingFire, shapeStealer); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + assertDamageReceived(playerA, shapeStealer, 4); + assertPowerToughness(playerA, shapeStealer, 4, 6); + assertDamageReceived(playerB, myojinOfCleansingFire, 4); + } } From d7458c5e35c39970b8c44d2f51df26a503b8d139 Mon Sep 17 00:00:00 2001 From: CountAndromalius Date: Fri, 16 Mar 2018 16:09:35 -0300 Subject: [PATCH 12/20] Implemented [DOM] Mox Amber (#4622) --- Mage.Sets/src/mage/cards/m/MoxAmber.java | 72 ++++++ .../AnyColorPermanentTypesManaAbility.java | 215 ++++++++++++++++++ 2 files changed, 287 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/MoxAmber.java create mode 100644 Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java diff --git a/Mage.Sets/src/mage/cards/m/MoxAmber.java b/Mage.Sets/src/mage/cards/m/MoxAmber.java new file mode 100644 index 00000000000..49414bc7e36 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoxAmber.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.m; + +import java.util.UUID; +import mage.abilities.mana.AnyColorPermanentTypesManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.SupertypePredicate; + +/** + * + * @author CountAndromalius + */ +public class MoxAmber extends CardImpl { + + public MoxAmber(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{0}"); + addSuperType(SuperType.LEGENDARY); + + // {tap}: Add one mana pool of any color among legendary creatures or planeswalkers you control. + FilterPermanent filter = new FilterPermanent("legendary creatures or planeswalkers"); + filter.add(Predicates.or( + Predicates.and( + new CardTypePredicate(CardType.CREATURE), + new SupertypePredicate(SuperType.LEGENDARY) + ), + new CardTypePredicate(CardType.PLANESWALKER)) + ); + this.addAbility(new AnyColorPermanentTypesManaAbility(TargetController.YOU, false, filter)); + } + + public MoxAmber(final MoxAmber card) { + super(card); + } + + @Override + public MoxAmber copy() { + return new MoxAmber(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java new file mode 100644 index 00000000000..e350b6ff233 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/mana/AnyColorPermanentTypesManaAbility.java @@ -0,0 +1,215 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.mana; + +import java.util.ArrayList; +import java.util.List; +import mage.Mana; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.ManaEffect; +import mage.choices.Choice; +import mage.choices.ChoiceColor; +import mage.constants.ColoredManaSymbol; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.ControllerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author CountAndromalius + */ +public class AnyColorPermanentTypesManaAbility extends ActivatedManaAbilityImpl { + + public AnyColorPermanentTypesManaAbility(TargetController targetController, FilterPermanent permanentTypes) { + this(targetController, true, permanentTypes); + } + + public AnyColorPermanentTypesManaAbility(TargetController targetController, boolean onlyColors, FilterPermanent permanentTypes) { + super(Zone.BATTLEFIELD, new AnyColorPermanentTypesManaEffect(targetController, onlyColors, permanentTypes), new TapSourceCost()); + } + + public AnyColorPermanentTypesManaAbility(final AnyColorPermanentTypesManaAbility ability) { + super(ability); + } + + @Override + public AnyColorPermanentTypesManaAbility copy() { + return new AnyColorPermanentTypesManaAbility(this); + } + + @Override + public List getNetMana(Game game) { + return ((AnyColorPermanentTypesManaEffect) getEffects().get(0)).getNetMana(game, this); + } + + @Override + public boolean definesMana(Game game) { + return true; + } + +} + +class AnyColorPermanentTypesManaEffect extends ManaEffect { + + private final FilterPermanent filter; + private final boolean onlyColors; // false if mana types can be produced (also Colorless mana), if false only colors can be produced (no Colorless mana). + + private boolean inManaTypeCalculation = false; + + public AnyColorPermanentTypesManaEffect(TargetController targetController, boolean onlyColors, FilterPermanent permanentTypes) { + super(); + filter = permanentTypes; + this.onlyColors = onlyColors; + filter.add(new ControllerPredicate(targetController)); + String text = targetController == TargetController.OPPONENT ? "an opponent controls." : "you control."; + staticText = "Add one mana of any " + (this.onlyColors ? "color" : "type") + " among " + permanentTypes.getMessage() + " " + text; + } + + public AnyColorPermanentTypesManaEffect(final AnyColorPermanentTypesManaEffect effect) { + super(effect); + this.filter = effect.filter.copy(); + this.onlyColors = effect.onlyColors; + } + + @Override + public boolean apply(Game game, Ability source) { + Mana types = getManaTypes(game, source); + Choice choice = new ChoiceColor(true); + choice.getChoices().clear(); + choice.setMessage("Pick a mana color"); + if (types.getBlack() > 0) { + choice.getChoices().add("Black"); + } + if (types.getRed() > 0) { + choice.getChoices().add("Red"); + } + if (types.getBlue() > 0) { + choice.getChoices().add("Blue"); + } + if (types.getGreen() > 0) { + choice.getChoices().add("Green"); + } + if (types.getWhite() > 0) { + choice.getChoices().add("White"); + } + if (!onlyColors && types.getColorless() > 0) { + choice.getChoices().add("Colorless"); + } + if (types.getAny() > 0) { + choice.getChoices().add("Black"); + choice.getChoices().add("Red"); + choice.getChoices().add("Blue"); + choice.getChoices().add("Green"); + choice.getChoices().add("White"); + if (!onlyColors) { + choice.getChoices().add("Colorless"); + } + + } + if (!choice.getChoices().isEmpty()) { + Player player = game.getPlayer(source.getControllerId()); + if (choice.getChoices().size() == 1) { + choice.setChoice(choice.getChoices().iterator().next()); + } else { + if (!player.choose(outcome, choice, game)) { + return false; + } + } + if (choice.getChoice() != null) { + Mana mana = new Mana(); + switch (choice.getChoice()) { + case "Black": + mana.setBlack(1); + break; + case "Blue": + mana.setBlue(1); + break; + case "Red": + mana.setRed(1); + break; + case "Green": + mana.setGreen(1); + break; + case "White": + mana.setWhite(1); + break; + case "Colorless": + mana.setColorless(1); + break; + } + checkToFirePossibleEvents(mana, game, source); + player.getManaPool().addMana(mana, game, source); + } + } + return true; + } + + @Override + public Mana getMana(Game game, Ability source) { + return null; + } + + private Mana getManaTypes(Game game, Ability source) { + Mana types = new Mana(); + if (game == null || game.getPhase() == null) { + return types; + } + if (inManaTypeCalculation) { + return types; + } + inManaTypeCalculation = true; + + List permanentColors; + // Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "needed to identify endless loop causing cards: {0}", source.getSourceObject(game).getName()); + List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game); + for (Permanent permanent : permanents) { + permanentColors = permanent.getColor(game).getColors(); + for (ObjectColor color : permanentColors){ + types.add(new Mana(color.getColoredManaSymbol())); + } + } + inManaTypeCalculation = false; + return types; + } + + public List getNetMana(Game game, Ability source) { + List netManas = new ArrayList<>(); + Mana types = getManaTypes(game, source); + if (types.getBlack() > 0) { + netManas.add(new Mana(ColoredManaSymbol.B)); + } + if (types.getRed() > 0) { + netManas.add(new Mana(ColoredManaSymbol.R)); + } + if (types.getBlue() > 0) { + netManas.add(new Mana(ColoredManaSymbol.U)); + } + if (types.getGreen() > 0) { + netManas.add(new Mana(ColoredManaSymbol.G)); + } + if (types.getWhite() > 0) { + netManas.add(new Mana(ColoredManaSymbol.W)); + } + if (types.getColorless() > 0) { + netManas.add(Mana.ColorlessMana(1)); + } + if (types.getAny() > 0) { + netManas.add(Mana.AnyMana(1)); + } + return netManas; + } + + @Override + public AnyColorPermanentTypesManaEffect copy() { + return new AnyColorPermanentTypesManaEffect(this); + } +} From 62db7ae72fd4ea6f91ab551555485128dfe868f6 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 16 Mar 2018 20:11:10 +0100 Subject: [PATCH 13/20] * Skyshrout War Beast - Fixed wrong P/T calculation because of wrong land filter. --- Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java | 9 +++------ Mage/src/main/java/mage/filter/StaticFilters.java | 8 ++++++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java b/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java index 25705180cda..7972d4fe375 100644 --- a/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java +++ b/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java @@ -36,7 +36,6 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.ChooseOpponentEffect; -import mage.constants.SubType; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -45,9 +44,9 @@ import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; +import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; @@ -107,9 +106,7 @@ class SkyshroudWarBeastEffect extends ContinuousEffectImpl { MageObject target = game.getObject(source.getSourceId()); if (target != null) { UUID playerId = (UUID) game.getState().getValue(source.getSourceId().toString() + ChooseOpponentEffect.VALUE_KEY); - FilterLandPermanent filter = new FilterLandPermanent(); - filter.add(new ControllerIdPredicate(playerId)); - int number = new PermanentsOnBattlefieldCount(filter).calculate(game, source, this); + int number = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_LANDS_NONBASIC).calculate(game, source, this); target.getPower().setValue(number); target.getToughness().setValue(number); return true; diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index e6ccc996578..a9894b4f590 100644 --- a/Mage/src/main/java/mage/filter/StaticFilters.java +++ b/Mage/src/main/java/mage/filter/StaticFilters.java @@ -179,13 +179,13 @@ public final class StaticFilters { static { FILTER_CONTROLLED_CREATURE.setLockedFilter(true); - } + } public static final FilterControlledCreaturePermanent FILTER_CONTROLLED_CREATURES = new FilterControlledCreaturePermanent("creatures you control"); static { FILTER_CONTROLLED_CREATURES.setLockedFilter(true); } - + public static final FilterControlledCreaturePermanent FILTER_CONTROLLED_A_CREATURE = new FilterControlledCreaturePermanent("a creature you control"); static { @@ -210,11 +210,15 @@ public final class StaticFilters { static { FILTER_LAND.setLockedFilter(true); } + public static final FilterLandPermanent FILTER_LANDS = new FilterLandPermanent("lands"); static { FILTER_LANDS.setLockedFilter(true); } + + public static final FilterLandPermanent FILTER_LANDS_NONBASIC = FilterLandPermanent.nonbasicLands(); + public static final FilterBasicLandCard FILTER_BASIC_LAND_CARD = new FilterBasicLandCard(); static { From 73dcb3ff224a2746bfae2c42d86853c6822c2a8d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 16 Mar 2018 20:14:28 +0100 Subject: [PATCH 14/20] * Skyshrout War Beast - Fixed wrong P/T calculation because of wrong land filter. --- Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java b/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java index 7972d4fe375..f74d9cb92da 100644 --- a/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java +++ b/Mage.Sets/src/mage/cards/s/SkyshroudWarBeast.java @@ -46,7 +46,8 @@ import mage.constants.Outcome; import mage.constants.SubLayer; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.StaticFilters; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.players.Player; @@ -106,7 +107,9 @@ class SkyshroudWarBeastEffect extends ContinuousEffectImpl { MageObject target = game.getObject(source.getSourceId()); if (target != null) { UUID playerId = (UUID) game.getState().getValue(source.getSourceId().toString() + ChooseOpponentEffect.VALUE_KEY); - int number = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_LANDS_NONBASIC).calculate(game, source, this); + FilterLandPermanent filter = FilterLandPermanent.nonbasicLand(); + filter.add(new ControllerIdPredicate(playerId)); + int number = new PermanentsOnBattlefieldCount(filter).calculate(game, source, this); target.getPower().setValue(number); target.getToughness().setValue(number); return true; From 8bc3efff07fc84373a8ac0dd7cfe30f02b8aa251 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 16 Mar 2018 14:58:11 -0500 Subject: [PATCH 15/20] - Added requested card Cytoshape. --- Mage.Sets/src/mage/cards/c/Cytoshape.java | 113 ++++++++++++++++++++++ Mage.Sets/src/mage/sets/Dissension.java | 1 + 2 files changed, 114 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/Cytoshape.java diff --git a/Mage.Sets/src/mage/cards/c/Cytoshape.java b/Mage.Sets/src/mage/cards/c/Cytoshape.java new file mode 100644 index 00000000000..72bddeb4f4b --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/Cytoshape.java @@ -0,0 +1,113 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.c; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SuperType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SupertypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanent; +import mage.util.functions.EmptyApplyToPermanent; + +/** + * + * @author jeffwadsworth + */ +public class Cytoshape extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonlegendary creature"); + + static { + filter.add(Predicates.not(new SupertypePredicate(SuperType.LEGENDARY))); + } + + public Cytoshape(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}{U}"); + + // Choose a nonlegendary creature on the battlefield. Target creature becomes a copy of that creature until end of turn. + this.getSpellAbility().addEffect(new CytoshapeEffect()); + Target target = new TargetCreaturePermanent(1, 1, filter, true); + target.setTargetTag(1); + this.getSpellAbility().addTarget(target); + + FilterCreaturePermanent filter2 = new FilterCreaturePermanent("target creature that will become a copy of that chosen creature"); + target = new TargetCreaturePermanent(filter2); + target.setTargetTag(2); + this.getSpellAbility().addTarget(target); + + } + + public Cytoshape(final Cytoshape card) { + super(card); + } + + @Override + public Cytoshape copy() { + return new Cytoshape(this); + } +} + +class CytoshapeEffect extends OneShotEffect { + + public CytoshapeEffect() { + super(Outcome.Copy); + this.staticText = "Choose a nonlegendary creature on the battlefield. Target creature becomes a copy of that creature until end of turn."; + } + + public CytoshapeEffect(final CytoshapeEffect effect) { + super(effect); + } + + @Override + public CytoshapeEffect copy() { + return new CytoshapeEffect(this); + } + + @Override + public boolean apply(Game game, Ability ability) { + Permanent copyFrom = game.getPermanent(getTargetPointer().getFirst(game, ability)); + if (copyFrom != null) { + Permanent copyTo = game.getPermanentOrLKIBattlefield(ability.getTargets().get(1).getFirstTarget()); + if (copyTo != null) { + game.copyPermanent(Duration.EndOfTurn, copyFrom, copyTo.getId(), ability, new EmptyApplyToPermanent()); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/Dissension.java b/Mage.Sets/src/mage/sets/Dissension.java index 58724c4ca45..3b8878f876d 100644 --- a/Mage.Sets/src/mage/sets/Dissension.java +++ b/Mage.Sets/src/mage/sets/Dissension.java @@ -85,6 +85,7 @@ public class Dissension extends ExpansionSet { cards.add(new SetCardInfo("Crypt Champion", 40, Rarity.UNCOMMON, mage.cards.c.CryptChampion.class)); cards.add(new SetCardInfo("Cytoplast Manipulator", 23, Rarity.RARE, mage.cards.c.CytoplastManipulator.class)); cards.add(new SetCardInfo("Cytoplast Root-Kin", 81, Rarity.RARE, mage.cards.c.CytoplastRootKin.class)); + cards.add(new SetCardInfo("Cytoshape", 108, Rarity.RARE, mage.cards.c.Cytoshape.class)); cards.add(new SetCardInfo("Cytospawn Shambler", 82, Rarity.COMMON, mage.cards.c.CytospawnShambler.class)); cards.add(new SetCardInfo("Delirium Skeins", 41, Rarity.COMMON, mage.cards.d.DeliriumSkeins.class)); cards.add(new SetCardInfo("Demonfire", 60, Rarity.RARE, mage.cards.d.Demonfire.class)); From 9c0d0a0153edfba3e09dcfbc7fc0325adaf2cb0a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 17 Mar 2018 00:46:12 +0100 Subject: [PATCH 16/20] * Saved table spectator setting to preferences. --- .../mage/client/dialog/NewTableDialog.java | 35 +++++++++++-------- .../client/dialog/NewTournamentDialog.java | 31 ++++++++-------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index 337475674ed..56a70a81860 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -27,6 +27,12 @@ */ package mage.client.dialog; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import javax.swing.*; import mage.cards.decks.importer.DeckImporterUtil; import mage.client.MageFrame; import mage.client.SessionHandler; @@ -45,13 +51,6 @@ import mage.view.GameTypeView; import mage.view.TableView; import org.apache.log4j.Logger; -import javax.swing.*; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - /** * @author BetaSteward_at_googlemail.com */ @@ -623,17 +622,22 @@ public class NewTableDialog extends MageDialog { * set the table settings from java prefs */ int currentSettingVersion = 0; + private void setGameSettingsFromPrefs(int version) { currentSettingVersion = version; String versionStr = ""; - if (currentSettingVersion == 1) { - versionStr = "1"; - btnPreviousConfiguration1.requestFocus(); - } else if (currentSettingVersion == 2) { - versionStr = "2"; - btnPreviousConfiguration2.requestFocus(); - } else { - btnPreviousConfiguration2.getParent().requestFocus(); + switch (currentSettingVersion) { + case 1: + versionStr = "1"; + btnPreviousConfiguration1.requestFocus(); + break; + case 2: + versionStr = "2"; + btnPreviousConfiguration2.requestFocus(); + break; + default: + btnPreviousConfiguration2.getParent().requestFocus(); + break; } txtName.setText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NAME + versionStr, "Game")); txtPassword.setText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD + versionStr, "")); @@ -724,6 +728,7 @@ public class NewTableDialog extends MageDialog { PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_RANGE + versionStr, Integer.toString(options.getRange().getRange())); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_ATTACK_OPTION + versionStr, options.getAttackOption().toString()); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_SKILL_LEVEL + versionStr, options.getSkillLevel().toString()); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_SPECTATORS_ALLOWED + versionStr, options.isSpectatorsAllowed() ? "Yes" : "No"); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_QUIT_RATIO + versionStr, Integer.toString(options.getQuitRatio())); StringBuilder playerTypesString = new StringBuilder(); for (Object player : players) { diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java index 17a376f4dca..5bf1e16a951 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java @@ -35,11 +35,13 @@ package mage.client.dialog; import java.awt.*; import java.io.File; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.UUID; import javax.swing.*; import javax.swing.filechooser.FileFilter; - import mage.cards.decks.Deck; import mage.cards.decks.importer.DeckImporterUtil; import mage.cards.repository.ExpansionInfo; @@ -589,9 +591,9 @@ public class NewTournamentDialog extends MageDialog { } else { for (JPanel panel : packPanels) { JComboBox combo = findComboInComponent(panel); - if(combo != null) { + if (combo != null) { tOptions.getLimitedOptions().getSetCodes().add(((ExpansionInfo) combo.getSelectedItem()).getCode()); - }else{ + } else { logger.error("Can't find combo component in " + panel.toString()); } } @@ -764,7 +766,6 @@ public class NewTournamentDialog extends MageDialog { this.spnNumPlayers.setModel(new SpinnerNumberModel(numPlayers, tournamentType.getMinPlayers(), tournamentType.getMaxPlayers(), 1)); this.spnNumPlayers.setEnabled(tournamentType.getMinPlayers() != tournamentType.getMaxPlayers()); createPlayers((Integer) spnNumPlayers.getValue() - 1); - this.spnNumSeats.setModel(new SpinnerNumberModel(2, 2, tournamentType.getMaxPlayers(), 1)); if (tournamentType.isLimited()) { @@ -926,7 +927,7 @@ public class NewTournamentDialog extends MageDialog { public void actionPerformed(java.awt.event.ActionEvent evt) { // search combo box near button (must be only one combo in panel) - JButton button = (JButton)evt.getSource(); + JButton button = (JButton) evt.getSource(); JComboBox combo = findComboInComponent(button.getParent()); if (combo != null) { @@ -941,12 +942,12 @@ public class NewTournamentDialog extends MageDialog { this.repaint(); } - private JComboBox findComboInComponent(Container panel){ + private JComboBox findComboInComponent(Container panel) { // search combo box near button (must be only one combo in panel) JComboBox combo = null; - for(Component comp: panel.getComponents()){ - if (comp instanceof JComboBox){ - combo = (JComboBox)comp; + for (Component comp : panel.getComponents()) { + if (comp instanceof JComboBox) { + combo = (JComboBox) comp; break; } } @@ -955,21 +956,21 @@ public class NewTournamentDialog extends MageDialog { private void packActionPerformed(java.awt.event.ActionEvent evt) { // fill all bottom combobox with same value - JComboBox curentCombo = (JComboBox)evt.getSource(); + JComboBox curentCombo = (JComboBox) evt.getSource(); int newValue = curentCombo.getSelectedIndex(); // search start index int startIndex = 0; - for(int i = 0; i < packPanels.size(); i++){ + for (int i = 0; i < packPanels.size(); i++) { JComboBox pack = findComboInComponent(packPanels.get(i)); - if (pack.equals(curentCombo)){ + if (pack.equals(curentCombo)) { startIndex = i + 1; break; } } // change all from start index - for(int i = startIndex; i < packPanels.size(); i++){ + for (int i = startIndex; i < packPanels.size(); i++) { JComboBox pack = findComboInComponent(packPanels.get(i)); pack.setSelectedIndex(newValue); } @@ -1128,7 +1129,7 @@ public class NewTournamentDialog extends MageDialog { break; } } - }else{ + } else { logger.error("Can't find combo component in " + panel.toString()); } } From af296cbe79b13a2825e977beabd15b74cecd60e6 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 17 Mar 2018 00:47:09 +0100 Subject: [PATCH 17/20] * Added display of live on player avatar image, --- .../mage/client/components/HoverButton.java | 46 ++++++++++++++- .../mage/client/dialog/PreferencesDialog.form | 48 +++++++++++----- .../mage/client/dialog/PreferencesDialog.java | 56 +++++++++++++------ .../java/mage/client/game/PlayerPanelExt.java | 18 ++---- 4 files changed, 122 insertions(+), 46 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/components/HoverButton.java b/Mage.Client/src/main/java/mage/client/components/HoverButton.java index bd288506cf8..7dbf3891525 100644 --- a/Mage.Client/src/main/java/mage/client/components/HoverButton.java +++ b/Mage.Client/src/main/java/mage/client/components/HoverButton.java @@ -3,6 +3,7 @@ package mage.client.components; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; +import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; @@ -40,6 +41,7 @@ public class HoverButton extends JPanel implements MouseListener { private String topText; private Image topTextImage; private Image topTextImageRight; + private String centerText; private boolean isHovered = false; private boolean isSelected = false; @@ -49,12 +51,15 @@ public class HoverButton extends JPanel implements MouseListener { private Command observer = null; private Command onHover = null; private Color textColor = Color.white; + private final Rectangle centerTextArea = new Rectangle(5, 18, 75, 40); + private final Color centerTextColor = Color.YELLOW; private final Color textBGColor = Color.black; static final Font textFont = new Font("Arial", Font.PLAIN, 12); static final Font textFontMini = new Font("Arial", Font.PLAIN, 11); static final Font textSetFontBoldMini = new Font("Arial", Font.BOLD, 12); static final Font textSetFontBold = new Font("Arial", Font.BOLD, 14); + private boolean useMiniFont = false; private boolean alignTextLeft = false; @@ -134,6 +139,21 @@ public class HoverButton extends JPanel implements MouseListener { if (topTextImageRight != null) { g.drawImage(topTextImageRight, this.getWidth() - 20, 3, this); } + + if (centerText != null) { + g2d.setColor(centerTextColor); + int fontSize = 40; + int val = Integer.parseInt(centerText); + if (val > 9999) { + fontSize = 24; + } else if (val > 999) { + fontSize = 28; + } else if (val > 99) { + fontSize = 34; + } + drawCenteredString(g2d, centerText, centerTextArea, new Font("Arial", Font.BOLD, fontSize)); + } + g2d.setColor(textColor); if (overlayImage != null) { g.drawImage(overlayImage, (imageSize.width - overlayImageSize.width) / 2, 10, this); } else if (set != null) { @@ -298,13 +318,17 @@ public class HoverButton extends JPanel implements MouseListener { public void setTopTextImage(Image topTextImage) { this.topTextImage = topTextImage; - this.textOffsetX = -1; // rest for new clculation + this.textOffsetX = -1; // rest for new calculation } public void setTopTextImageRight(Image topTextImage) { this.topTextImageRight = topTextImage; } + public void setCenterText(String centerText) { + this.centerText = centerText; + } + public void setTextAlwaysVisible(boolean textAlwaysVisible) { this.textAlwaysVisible = textAlwaysVisible; } @@ -313,4 +337,24 @@ public class HoverButton extends JPanel implements MouseListener { this.alignTextLeft = alignTextLeft; } + /** + * Draw a String centered in the middle of a Rectangle. + * + * @param g The Graphics instance. + * @param text The String to draw. + * @param rect The Rectangle to center the text in. + * @param font + */ + public void drawCenteredString(Graphics g, String text, Rectangle rect, Font font) { + // Get the FontMetrics + FontMetrics metrics = g.getFontMetrics(font); + // Determine the X coordinate for the text + int x = rect.x + (rect.width - metrics.stringWidth(text)) / 2; + // Determine the Y coordinate for the text (note we add the ascent, as in java 2d 0 is top of the screen) + int y = rect.y + ((rect.height - metrics.getHeight()) / 2) + metrics.getAscent(); + // Set the font + g.setFont(font); + // Draw the String + g.drawString(text, x, y); + } } 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 fc3c45de371..f122cdc1c44 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form @@ -95,10 +95,10 @@ - + - + @@ -200,7 +200,7 @@ - + @@ -295,16 +295,22 @@ - - - - - - - - + + + + + + + + + + + + + + - + @@ -315,6 +321,8 @@ + + @@ -324,7 +332,6 @@ - @@ -354,6 +361,17 @@ + + + + + + + + + + + @@ -4274,7 +4292,7 @@ - + @@ -4847,7 +4865,7 @@ - + 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 ba94fc5d2f9..76aeb4619e4 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -96,6 +96,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_SHOW_FULL_IMAGE_PATH = "showFullImagePath"; public static final String KEY_PERMANENTS_IN_ONE_PILE = "nonLandPermanentsInOnePile"; public static final String KEY_SHOW_PLAYER_NAMES_PERMANENTLY = "showPlayerNamesPermanently"; + 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"; @@ -421,6 +422,7 @@ public class PreferencesDialog extends javax.swing.JDialog { main_game = new javax.swing.JPanel(); nonLandPermanentsInOnePile = new javax.swing.JCheckBox(); showPlayerNamesPermanently = new javax.swing.JCheckBox(); + displayLifeOnAvatar = new javax.swing.JCheckBox(); showAbilityPickerForced = new javax.swing.JCheckBox(); cbAllowRequestToShowHandCards = new javax.swing.JCheckBox(); cbShowStormCounter = new javax.swing.JCheckBox(); @@ -700,7 +702,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .add(6, 6, 6) .add(main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false) - .add(tooltipDelayLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 308, Short.MAX_VALUE) + .add(tooltipDelayLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(tooltipDelay, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .add(main_cardLayout.createSequentialGroup() .add(showCardName) @@ -741,6 +743,16 @@ public class PreferencesDialog extends javax.swing.JDialog { } }); + displayLifeOnAvatar.setSelected(true); + displayLifeOnAvatar.setText("Display life on avatar image"); + displayLifeOnAvatar.setToolTipText("Display the player's life over its avatar image."); + displayLifeOnAvatar.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + 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 abilities or spells without costs"); showAbilityPickerForced.setToolTipText("This prevents you from accidently activating abilities without other costs than tapping or casting spells with 0 mana costs."); @@ -797,15 +809,19 @@ public class PreferencesDialog extends javax.swing.JDialog { main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(main_gameLayout.createSequentialGroup() .addContainerGap() - .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(cbAskMoveToGraveOrder, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(showAbilityPickerForced, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addContainerGap(177, Short.MAX_VALUE)) + .add(main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(main_gameLayout.createSequentialGroup() + .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(cbAskMoveToGraveOrder, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(showAbilityPickerForced, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .add(0, 0, Short.MAX_VALUE)) + .add(displayLifeOnAvatar, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) ); main_gameLayout.setVerticalGroup( main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) @@ -814,6 +830,8 @@ public class PreferencesDialog extends javax.swing.JDialog { .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(showPlayerNamesPermanently) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(displayLifeOnAvatar) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(showAbilityPickerForced) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(cbAllowRequestToShowHandCards) @@ -822,8 +840,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(cbConfirmEmptyManaPool) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(cbAskMoveToGraveOrder) - .addContainerGap()) + .add(cbAskMoveToGraveOrder)) ); nonLandPermanentsInOnePile.getAccessibleContext().setAccessibleName("nonLandPermanentsInOnePile"); @@ -880,10 +897,10 @@ public class PreferencesDialog extends javax.swing.JDialog { .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(main_game, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(main_gamelog, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 107, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(main_gamelog, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(main_battlefield, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addContainerGap(121, Short.MAX_VALUE)) + .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); main_card.getAccessibleContext().setAccessibleName("Game panel"); @@ -1797,7 +1814,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .add(panelCardImages, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(panelBackgroundImages, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addContainerGap(125, Short.MAX_VALUE)) + .addContainerGap(133, Short.MAX_VALUE)) ); tabsPanel.addTab("Images", tabImages); @@ -2372,7 +2389,7 @@ public class PreferencesDialog extends javax.swing.JDialog { tabAvatarsLayout.setVerticalGroup( tabAvatarsLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(tabAvatarsLayout.createSequentialGroup() - .add(avatarPane, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 584, Short.MAX_VALUE) + .add(avatarPane, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 620, Short.MAX_VALUE) .addContainerGap()) ); @@ -2762,6 +2779,7 @@ public class PreferencesDialog extends javax.swing.JDialog { save(prefs, dialog.showFullImagePath, KEY_SHOW_FULL_IMAGE_PATH, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.nonLandPermanentsInOnePile, KEY_PERMANENTS_IN_ONE_PILE, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.showPlayerNamesPermanently, KEY_SHOW_PLAYER_NAMES_PERMANENTLY, "true", "false", UPDATE_CACHE_POLICY); + 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); @@ -3225,6 +3243,10 @@ public class PreferencesDialog extends javax.swing.JDialog { } }//GEN-LAST:event_cbGameJsonLogAutoSaveActionPerformed + private void displayLifeOnAvatarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_displayLifeOnAvatarActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_displayLifeOnAvatarActionPerformed + private void showProxySettings() { Connection.ProxyType proxyType = (Connection.ProxyType) cbProxyType.getSelectedItem(); switch (proxyType) { @@ -3331,6 +3353,7 @@ public class PreferencesDialog extends javax.swing.JDialog { load(prefs, dialog.showFullImagePath, KEY_SHOW_FULL_IMAGE_PATH, "true"); load(prefs, dialog.nonLandPermanentsInOnePile, KEY_PERMANENTS_IN_ONE_PILE, "true"); load(prefs, dialog.showPlayerNamesPermanently, KEY_SHOW_PLAYER_NAMES_PERMANENTLY, "true"); + 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"); @@ -3928,6 +3951,7 @@ public class PreferencesDialog extends javax.swing.JDialog { private javax.swing.JCheckBox checkBoxUpkeepYou; private javax.swing.JPanel connection_servers; private javax.swing.JLabel controlsDescriptionLabel; + private javax.swing.JCheckBox displayLifeOnAvatar; private javax.swing.JButton exitButton; private javax.swing.JLabel fontSizeLabel; private javax.swing.JPanel guiSizeBasic; 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 5722b74785d..cf7b843556e 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java @@ -77,7 +77,6 @@ import mage.constants.ManaType; import mage.counters.Counter; import mage.counters.CounterType; import mage.designations.DesignationType; -import mage.remote.Session; import mage.utils.timer.PriorityTimer; import mage.view.CardView; import mage.view.ManaPoolView; @@ -93,13 +92,10 @@ public class PlayerPanelExt extends javax.swing.JPanel { private UUID playerId; private UUID gameId; - private Session session; private PlayerView player; private BigCard bigCard; - private static final int AVATAR_COUNT = 77; - private static final String DEFAULT_AVATAR_PATH = "/avatars/" + DEFAULT_AVATAR_ID + ".jpg"; private static final int PANEL_WIDTH = 94; @@ -179,8 +175,11 @@ public class PlayerPanelExt extends javax.swing.JPanel { public void update(PlayerView player) { this.player = player; - updateAvatar(); int playerLife = player.getLife(); + avatar.setCenterText("true".equals(MageFrame.getPreferences().get(PreferencesDialog.KEY_DISPLAY_LIVE_ON_AVATAR, "true")) + ? String.valueOf(playerLife) : null); + updateAvatar(); + if (playerLife > 99) { Font font = lifeLabel.getFont(); font = font.deriveFont(9f); @@ -701,8 +700,6 @@ public class PlayerPanelExt extends javax.swing.JPanel { .addComponent(btnPlayer, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(timerLabel, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(avatar, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 80, Short.MAX_VALUE)) - // .addGroup(gl_panelBackground.createSequentialGroup() - // .addComponent(avatarFlag, GroupLayout.PREFERRED_SIZE, 16, GroupLayout.PREFERRED_SIZE)) .addGap(8)) .addGroup(gl_panelBackground.createSequentialGroup() .addGap(6) @@ -824,16 +821,12 @@ public class PlayerPanelExt extends javax.swing.JPanel { protected void sizePlayerPanel(boolean smallMode) { if (smallMode) { avatar.setVisible(false); -// avatarFlag.setVisible(false); -// monarchIcon.setVisible(false); btnPlayer.setVisible(true); timerLabel.setVisible(true); panelBackground.setPreferredSize(new Dimension(PANEL_WIDTH - 2, PANEL_HEIGHT_SMALL)); panelBackground.setBounds(0, 0, PANEL_WIDTH - 2, PANEL_HEIGHT_SMALL); } else { avatar.setVisible(true); -// avatarFlag.setVisible(true); -// monarchIcon.setVisible(true); btnPlayer.setVisible(false); timerLabel.setVisible(false); panelBackground.setPreferredSize(new Dimension(PANEL_WIDTH - 2, PANEL_HEIGHT)); @@ -887,8 +880,6 @@ public class PlayerPanelExt extends javax.swing.JPanel { } private HoverButton avatar; -// private JLabel avatarFlag; -// private JLabel monarchIcon; private JButton btnPlayer; private ImagePanel life; private ImagePanel poison; @@ -918,7 +909,6 @@ public class PlayerPanelExt extends javax.swing.JPanel { private JPanel energyExperiencePanel; private HoverButton exileZone; private HoverButton commandZone; - private HoverButton enchantPlayerViewZone; private final Map manaLabels = new HashMap<>(); } From 170ad083c75bc7aeb66dc7d304d61a9fb549c4d4 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 17 Mar 2018 01:51:25 +0100 Subject: [PATCH 18/20] * Fixed a problem of the AI that it could cast cards with flashback without paying the costs. --- .../Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java | 2 +- .../main/java/mage/abilities/keyword/FlashbackAbility.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java index 287b31752c6..a7c5a914c97 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java @@ -617,7 +617,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { } // end of for (allActions) if (depth == maxDepth) { - logger.info(new StringBuilder("Sim Prio [").append(depth).append("] -- End for Max Depth -- Nodes calculated: ").append(SimulationNode2.nodeCount)); + logger.info("Sim Prio [" + depth + "] -- End for Max Depth -- Nodes calculated: " + SimulationNode2.nodeCount); } if (bestNode != null) { node.children.clear(); diff --git a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java index aa457aea70a..13f489dd365 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java @@ -126,8 +126,8 @@ public class FlashbackAbility extends SpellAbility { spellAbilityCopy.setId(this.getId()); spellAbilityCopy.getManaCosts().clear(); spellAbilityCopy.getManaCostsToPay().clear(); - spellAbilityCopy.getCosts().addAll(this.getCosts()); - spellAbilityCopy.addCost(this.getManaCosts()); + spellAbilityCopy.getCosts().addAll(this.getCosts().copy()); + spellAbilityCopy.addCost(this.getManaCosts().copy()); spellAbilityCopy.setSpellAbilityCastMode(this.getSpellAbilityCastMode()); spellAbilityToResolve = spellAbilityCopy; ContinuousEffect effect = new FlashbackReplacementEffect(); From d9ede3585774ca95a4fddbdd0589ade9ba19868f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 17 Mar 2018 12:35:40 +0100 Subject: [PATCH 19/20] * MIracle - Fixed a problem with miracle casting costs. --- .../src/mage/cards/e/EntreatTheAngels.java | 7 +- .../abilities/keyword/MiracleAbility.java | 91 ++++++++++--------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/Mage.Sets/src/mage/cards/e/EntreatTheAngels.java b/Mage.Sets/src/mage/cards/e/EntreatTheAngels.java index 8c6b068c01d..e0a0f4cc418 100644 --- a/Mage.Sets/src/mage/cards/e/EntreatTheAngels.java +++ b/Mage.Sets/src/mage/cards/e/EntreatTheAngels.java @@ -40,15 +40,14 @@ import mage.game.permanent.token.AngelToken; /** * * @author noxx - + * */ public class EntreatTheAngels extends CardImpl { public EntreatTheAngels(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{X}{W}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{W}{W}{W}"); - - // create X 4/4 white Angel creature tokens with flying. + // Create X 4/4 white Angel creature tokens with flying. this.getSpellAbility().addEffect(new CreateTokenEffect(new AngelToken(), new ManacostVariableValue())); // Miracle {X}{W}{W} diff --git a/Mage/src/main/java/mage/abilities/keyword/MiracleAbility.java b/Mage/src/main/java/mage/abilities/keyword/MiracleAbility.java index 20996fb6625..8c50e898c0a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MiracleAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MiracleAbility.java @@ -24,11 +24,11 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ - + */ package mage.abilities.keyword; import mage.abilities.Ability; +import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; @@ -45,61 +45,67 @@ import mage.watchers.common.MiracleWatcher; /** * 702.92. Miracle * - * 702.92a Miracle is a static ability linked to a triggered ability (see rule 603.10). - * "Miracle [cost]" means "You may reveal this card from your hand as you draw it if - * it's the first card you've drawn this turn. When you reveal this card this way, - * you may cast it by paying [cost] rather than its mana cost." + * 702.92a Miracle is a static ability linked to a triggered ability (see rule + * 603.10). "Miracle [cost]" means "You may reveal this card from your hand as + * you draw it if it's the first card you've drawn this turn. When you reveal + * this card this way, you may cast it by paying [cost] rather than its mana + * cost." * - * 702.92b If a player chooses to reveal a card using its miracle ability, he or she - * plays with that card revealed until that card leaves his or her hand, that ability - * resolves, or that ability otherwise leaves the stack. + * 702.92b If a player chooses to reveal a card using its miracle ability, he or + * she plays with that card revealed until that card leaves his or her hand, + * that ability resolves, or that ability otherwise leaves the stack. * - * You can cast a card for its miracle cost only as the miracle triggered ability resolves. - * If you don't want to cast it at that time (or you can't cast it, perhaps because - * there are no legal targets available), you won't be able to cast it later for the miracle cost. + * You can cast a card for its miracle cost only as the miracle triggered + * ability resolves. If you don't want to cast it at that time (or you can't + * cast it, perhaps because there are no legal targets available), you won't be + * able to cast it later for the miracle cost. * - * RULINGS: - * You still draw the card, whether you use the miracle ability or not. Any ability that - * triggers whenever you draw a card, for example, will trigger. If you don't cast the card - * using its miracle ability, it will remain in your hand. + * RULINGS: You still draw the card, whether you use the miracle ability or not. + * Any ability that triggers whenever you draw a card, for example, will + * trigger. If you don't cast the card using its miracle ability, it will remain + * in your hand. * - * You can reveal and cast a card with miracle on any turn, not just your own, if it's the - * first card you've drawn that turn. + * You can reveal and cast a card with miracle on any turn, not just your own, + * if it's the first card you've drawn that turn. * - * You don't have to reveal a drawn card with miracle if you don't wish to cast it at that time. + * You don't have to reveal a drawn card with miracle if you don't wish to cast + * it at that time. * - * You can cast a card for its miracle cost only as the miracle triggered ability resolves. - * If you don't want to cast it at that time (or you can't cast it, perhaps because there are - * no legal targets available), you won't be able to cast it later for the miracle cost. + * You can cast a card for its miracle cost only as the miracle triggered + * ability resolves. If you don't want to cast it at that time (or you can't + * cast it, perhaps because there are no legal targets available), you won't be + * able to cast it later for the miracle cost. * - * You cast the card with miracle during the resolution of the triggered ability. Ignore any timing - * restrictions based on the card's type. + * You cast the card with miracle during the resolution of the triggered + * ability. Ignore any timing restrictions based on the card's type. * - * It's important to reveal a card with miracle before it is mixed with the other cards in your hand. + * It's important to reveal a card with miracle before it is mixed with the + * other cards in your hand. * - * Multiple card draws are always treated as a sequence of individual card draws. For example, if - * you haven't drawn any cards yet during a turn and cast a spell that instructs you to draw three - * cards, you'll draw them one at a time. Only the first card drawn this way may be revealed and cast - * using its miracle ability. + * Multiple card draws are always treated as a sequence of individual card + * draws. For example, if you haven't drawn any cards yet during a turn and cast + * a spell that instructs you to draw three cards, you'll draw them one at a + * time. Only the first card drawn this way may be revealed and cast using its + * miracle ability. * - * If the card with miracle leaves your hand before the triggered ability resolves, you won't be able - * to cast it using its miracle ability. + * If the card with miracle leaves your hand before the triggered ability + * resolves, you won't be able to cast it using its miracle ability. * - * You draw your opening hand before any turn begins. Cards you draw for your opening hand - * can't be cast using miracle. + * You draw your opening hand before any turn begins. Cards you draw for your + * opening hand can't be cast using miracle. * * @author noxx, LevelX2 */ - public class MiracleAbility extends TriggeredAbilityImpl { + private static final String staticRule = " (You may cast this card for its miracle cost when you draw it if it's the first card you drew this turn.)"; private String ruleText; @SuppressWarnings("unchecked") public MiracleAbility(Card card, ManaCosts miracleCosts) { - super(Zone.HAND, new MiracleEffect((ManaCosts)miracleCosts), true); - addWatcher(new MiracleWatcher()); - ruleText = "Miracle " + miracleCosts.getText() + staticRule; + super(Zone.HAND, new MiracleEffect((ManaCosts) miracleCosts), true); + addWatcher(new MiracleWatcher()); + ruleText = "Miracle " + miracleCosts.getText() + staticRule; } public MiracleAbility(final MiracleAbility ability) { @@ -161,17 +167,12 @@ class MiracleEffect extends OneShotEffect { // use target pointer here, so it's the same card that triggered the event (not gone back to library e.g.) Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (controller != null && card != null) { - ManaCosts costRef = card.getSpellAbility().getManaCostsToPay(); + SpellAbility abilityToCast = card.getSpellAbility().copy(); + ManaCosts costRef = abilityToCast.getManaCostsToPay(); // replace with the new cost costRef.clear(); costRef.add(miracleCosts); - controller.cast(card.getSpellAbility(), game, false); - - // Reset the casting costs (in case the player cancels cast and plays the card later) - costRef.clear(); - for (ManaCost manaCost : card.getSpellAbility().getManaCosts()) { - costRef.add(manaCost); - } + controller.cast(abilityToCast, game, false); return true; } return false; From 66bd5294e8332b1bb2a871ad1c25c316c3ef849c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 17 Mar 2018 13:43:31 +0100 Subject: [PATCH 20/20] * Removed effect's ApplyEffectsAfter functionality. It's now always applied. --- .../src/mage/cards/a/AcrobaticManeuver.java | 3 +- Mage.Sets/src/mage/cards/a/ActOfTreason.java | 10 +++---- Mage.Sets/src/mage/cards/a/Ambuscade.java | 4 +-- .../src/mage/cards/b/BarrelDownSokenzan.java | 5 ++-- .../src/mage/cards/b/BragoKingEternal.java | 3 +- Mage.Sets/src/mage/cards/c/CauldronHaze.java | 9 +++--- .../mage/cards/c/ChargeAcrossTheAraba.java | 29 +++++++++---------- Mage.Sets/src/mage/cards/c/ClearShot.java | 1 - Mage.Sets/src/mage/cards/c/Cloudshift.java | 3 +- .../src/mage/cards/d/DromokasCommand.java | 2 -- .../src/mage/cards/e/EldraziDisplacer.java | 3 +- .../src/mage/cards/e/EpicConfrontation.java | 8 ++--- Mage.Sets/src/mage/cards/e/EssenceFlux.java | 6 ++-- .../src/mage/cards/f/FelidarGuardian.java | 1 - Mage.Sets/src/mage/cards/g/GrabTheReins.java | 14 ++++----- Mage.Sets/src/mage/cards/h/HuntTheHunter.java | 9 +++--- Mage.Sets/src/mage/cards/h/HuntTheWeak.java | 5 ++-- .../src/mage/cards/n/NissasJudgment.java | 3 +- .../src/mage/cards/p/PlowThroughReito.java | 7 ++--- Mage.Sets/src/mage/cards/s/SavagePunch.java | 6 ++-- Mage.Sets/src/mage/cards/s/SavageStomp.java | 1 - .../src/mage/cards/s/SinkIntoTakenuma.java | 5 ++-- .../src/mage/cards/s/StartYourEngines.java | 6 ++-- Mage.Sets/src/mage/cards/s/SwiftKick.java | 11 ++++--- Mage.Sets/src/mage/cards/s/SylvanLibrary.java | 2 +- Mage.Sets/src/mage/cards/t/TemurCharm.java | 18 +++++------- Mage.Sets/src/mage/cards/w/WildInstincts.java | 3 +- .../src/mage/cards/w/WispweaverAngel.java | 5 ++-- .../src/mage/cards/z/ZealousConscripts.java | 2 +- .../main/java/mage/abilities/AbilityImpl.java | 6 ++-- .../dynamicvalue/common/SweepNumber.java | 27 ++++------------- .../java/mage/abilities/effects/Effect.java | 4 --- .../mage/abilities/effects/EffectImpl.java | 14 --------- .../continuous/BoostControlledEffect.java | 9 +++--- .../effects/keyword/SweepEffect.java | 2 +- .../abilities/keyword/InspiredAbility.java | 8 ++--- 36 files changed, 92 insertions(+), 162 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AcrobaticManeuver.java b/Mage.Sets/src/mage/cards/a/AcrobaticManeuver.java index 79d5c8820ce..dc93cd129d6 100644 --- a/Mage.Sets/src/mage/cards/a/AcrobaticManeuver.java +++ b/Mage.Sets/src/mage/cards/a/AcrobaticManeuver.java @@ -44,12 +44,11 @@ import mage.target.common.TargetControlledCreaturePermanent; public class AcrobaticManeuver extends CardImpl { public AcrobaticManeuver(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); // Exile target creature you control, then return that card to the battlefield under its owner's control. this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); Effect effect = new ExileTargetForSourceEffect(); - effect.setApplyEffectsAfter(); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/a/ActOfTreason.java b/Mage.Sets/src/mage/cards/a/ActOfTreason.java index bed4e24e0c3..d8285a01dcb 100644 --- a/Mage.Sets/src/mage/cards/a/ActOfTreason.java +++ b/Mage.Sets/src/mage/cards/a/ActOfTreason.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. diff --git a/Mage.Sets/src/mage/cards/a/Ambuscade.java b/Mage.Sets/src/mage/cards/a/Ambuscade.java index dc4f37d9670..e97592cb703 100644 --- a/Mage.Sets/src/mage/cards/a/Ambuscade.java +++ b/Mage.Sets/src/mage/cards/a/Ambuscade.java @@ -52,14 +52,12 @@ public class Ambuscade extends CardImpl { static { filter.add(new ControllerPredicate(TargetController.NOT_YOU)); } - + public Ambuscade(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); - // Target creature you control gets +1/+0 until end of turn. Effect effect = new BoostTargetEffect(1, 0, Duration.EndOfTurn); - effect.setApplyEffectsAfter(); // needed to count the boost for the second effect this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/b/BarrelDownSokenzan.java b/Mage.Sets/src/mage/cards/b/BarrelDownSokenzan.java index 931cc8fe137..ae2f5fedbe3 100644 --- a/Mage.Sets/src/mage/cards/b/BarrelDownSokenzan.java +++ b/Mage.Sets/src/mage/cards/b/BarrelDownSokenzan.java @@ -46,13 +46,12 @@ import mage.target.common.TargetCreaturePermanent; public class BarrelDownSokenzan extends CardImpl { public BarrelDownSokenzan(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); this.subtype.add(SubType.ARCANE); - // Sweep - Return any number of Mountains you control to their owner's hand. Barrel Down Sokenzan deals damage to target creature equal to twice the number of Mountains returned this way. this.getSpellAbility().addEffect(new SweepEffect(SubType.MOUNTAIN)); - DynamicValue sweepValue = new MultipliedValue(new SweepNumber("Mountain", false), 2); + DynamicValue sweepValue = new MultipliedValue(new SweepNumber("Mountain"), 2); this.getSpellAbility().addEffect(new DamageTargetEffect(sweepValue)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/b/BragoKingEternal.java b/Mage.Sets/src/mage/cards/b/BragoKingEternal.java index 91743d6bcf9..c69b88afc32 100644 --- a/Mage.Sets/src/mage/cards/b/BragoKingEternal.java +++ b/Mage.Sets/src/mage/cards/b/BragoKingEternal.java @@ -53,7 +53,7 @@ import mage.target.common.TargetControlledPermanent; public class BragoKingEternal extends CardImpl { public BragoKingEternal(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}"); addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.SPIRIT); @@ -65,7 +65,6 @@ public class BragoKingEternal extends CardImpl { // When Brago, King Eternal deals combat damage to a player, exile any number of target nonland permanents you control, then return those cards to the battlefield under their owner's control. Effect effect = new ExileTargetEffect(this.getId(), this.getName(), Zone.BATTLEFIELD); effect.setText("exile any number of target nonland permanents you control"); - effect.setApplyEffectsAfter(); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, false); FilterControlledPermanent filterControlledNonlandPermanent = new FilterControlledPermanent(); filterControlledNonlandPermanent.add(Predicates.not(new CardTypePredicate(CardType.LAND))); diff --git a/Mage.Sets/src/mage/cards/c/CauldronHaze.java b/Mage.Sets/src/mage/cards/c/CauldronHaze.java index 7f3fbe6ed50..6521ec0c8c2 100644 --- a/Mage.Sets/src/mage/cards/c/CauldronHaze.java +++ b/Mage.Sets/src/mage/cards/c/CauldronHaze.java @@ -41,17 +41,16 @@ import mage.target.common.TargetCreaturePermanent; * @author jeffwadsworth */ public class CauldronHaze extends CardImpl { - - private String rule = "Choose any number of target creatures. Each of those creatures gains persist until end of turn"; + + private final String rule = "Choose any number of target creatures. Each of those creatures gains persist until end of turn"; public CauldronHaze(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W/B}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W/B}"); // Choose any number of target creatures. Each of those creatures gains persist until end of turn. this.getSpellAbility().addEffect(new GainAbilityTargetEffect(new PersistAbility(), Duration.EndOfTurn, rule)); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE)); - + } public CauldronHaze(final CauldronHaze card) { diff --git a/Mage.Sets/src/mage/cards/c/ChargeAcrossTheAraba.java b/Mage.Sets/src/mage/cards/c/ChargeAcrossTheAraba.java index 9cbb73c1995..d7467b824a4 100644 --- a/Mage.Sets/src/mage/cards/c/ChargeAcrossTheAraba.java +++ b/Mage.Sets/src/mage/cards/c/ChargeAcrossTheAraba.java @@ -1,4 +1,4 @@ - /* +/* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -27,16 +27,16 @@ */ package mage.cards.c; - import java.util.UUID; - import mage.abilities.dynamicvalue.DynamicValue; - import mage.abilities.dynamicvalue.common.SweepNumber; - import mage.abilities.effects.common.continuous.BoostControlledEffect; - import mage.abilities.effects.keyword.SweepEffect; - import mage.cards.CardImpl; - import mage.cards.CardSetInfo; - import mage.constants.CardType; - import mage.constants.Duration; - import mage.constants.SubType; +import java.util.UUID; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.SweepNumber; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.keyword.SweepEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; /** * @@ -45,14 +45,13 @@ package mage.cards.c; public class ChargeAcrossTheAraba extends CardImpl { public ChargeAcrossTheAraba(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{4}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{W}"); this.subtype.add(SubType.ARCANE); - // Sweep - Return any number of Plains you control to their owner's hand. Creatures you control get +1/+1 until end of turn for each Plains returned this way. this.getSpellAbility().addEffect(new SweepEffect(SubType.PLAINS)); - DynamicValue sweepValue = new SweepNumber("Plains", true); - this.getSpellAbility().addEffect(new BoostControlledEffect(sweepValue, sweepValue, Duration.EndOfTurn)); + DynamicValue sweepValue = new SweepNumber("Plains"); + this.getSpellAbility().addEffect(new BoostControlledEffect(sweepValue, sweepValue, Duration.EndOfTurn, null, false, true)); } diff --git a/Mage.Sets/src/mage/cards/c/ClearShot.java b/Mage.Sets/src/mage/cards/c/ClearShot.java index cab3685ca7a..65597c5b025 100644 --- a/Mage.Sets/src/mage/cards/c/ClearShot.java +++ b/Mage.Sets/src/mage/cards/c/ClearShot.java @@ -58,7 +58,6 @@ public class ClearShot extends CardImpl { // Target creature you control gets +1/+1 until end of turn. Effect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); - effect.setApplyEffectsAfter(); // needed to count the boost for the second effect this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/c/Cloudshift.java b/Mage.Sets/src/mage/cards/c/Cloudshift.java index b1119bd4b9a..5f6941023f7 100644 --- a/Mage.Sets/src/mage/cards/c/Cloudshift.java +++ b/Mage.Sets/src/mage/cards/c/Cloudshift.java @@ -43,12 +43,11 @@ import mage.target.common.TargetControlledCreaturePermanent; public class Cloudshift extends CardImpl { public Cloudshift(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); // Exile target creature you control, then return that card to the battlefield under your control. this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); Effect effect = new ExileTargetForSourceEffect(); - effect.setApplyEffectsAfter(); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(true)); } diff --git a/Mage.Sets/src/mage/cards/d/DromokasCommand.java b/Mage.Sets/src/mage/cards/d/DromokasCommand.java index 51bb89c387e..f825b0d34c0 100644 --- a/Mage.Sets/src/mage/cards/d/DromokasCommand.java +++ b/Mage.Sets/src/mage/cards/d/DromokasCommand.java @@ -78,7 +78,6 @@ public class DromokasCommand extends CardImpl { Mode mode = new Mode(); Effect effect = new SacrificeEffect(filterEnchantment, 1, "target player"); effect.setText("Target player sacrifices an enchantment"); - effect.setApplyEffectsAfter(); // so P/T chnaging effects take place before the fighting effect is applied mode.getEffects().add(effect); mode.getTargets().add(new TargetPlayer()); this.getSpellAbility().getModes().addMode(mode); @@ -87,7 +86,6 @@ public class DromokasCommand extends CardImpl { mode = new Mode(); effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); effect.setText("Put a +1/+1 counter on target creature"); - effect.setApplyEffectsAfter(); // so the counter is taken into account if the target is also used in mode 4 mode.getEffects().add(effect); mode.getTargets().add(new TargetCreaturePermanent(filterCreature)); this.getSpellAbility().getModes().addMode(mode); diff --git a/Mage.Sets/src/mage/cards/e/EldraziDisplacer.java b/Mage.Sets/src/mage/cards/e/EldraziDisplacer.java index ceaa7cfbf9b..1252e16a38a 100644 --- a/Mage.Sets/src/mage/cards/e/EldraziDisplacer.java +++ b/Mage.Sets/src/mage/cards/e/EldraziDisplacer.java @@ -58,7 +58,7 @@ public class EldraziDisplacer extends CardImpl { } public EldraziDisplacer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.ELDRAZI); this.power = new MageInt(3); this.toughness = new MageInt(3); @@ -69,7 +69,6 @@ public class EldraziDisplacer extends CardImpl { // {2}{C}: Exile another target creature, then return it to the battlefield tapped under its owner's control. Effect effect = new ExileTargetForSourceEffect(); effect.setText("Exile another target creature"); - effect.setApplyEffectsAfter(); // Needed to let temporary continuous effects end if a permanent is blinked Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl<>("{2}{C}")); effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(true); effect.setText(", then return it to the battlefield tapped under its owner's control"); diff --git a/Mage.Sets/src/mage/cards/e/EpicConfrontation.java b/Mage.Sets/src/mage/cards/e/EpicConfrontation.java index 30f6f9e8842..8fa809a30e1 100644 --- a/Mage.Sets/src/mage/cards/e/EpicConfrontation.java +++ b/Mage.Sets/src/mage/cards/e/EpicConfrontation.java @@ -47,6 +47,7 @@ import mage.target.common.TargetCreaturePermanent; * @author fireshoes */ public class EpicConfrontation extends CardImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); static { @@ -54,18 +55,17 @@ public class EpicConfrontation extends CardImpl { } public EpicConfrontation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); // Target creature you control gets +1/+2 until end of turn. It fights target creature you don't control. - Effect effect = new BoostTargetEffect(1,2,Duration.EndOfTurn); - effect.setApplyEffectsAfter(); + Effect effect = new BoostTargetEffect(1, 2, Duration.EndOfTurn); this.getSpellAbility().addEffect(effect); effect = new FightTargetsEffect(); effect.setText("It fights target creature you don't control"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); Target target = new TargetCreaturePermanent(filter); - this.getSpellAbility().addTarget(target); + this.getSpellAbility().addTarget(target); } public EpicConfrontation(final EpicConfrontation card) { diff --git a/Mage.Sets/src/mage/cards/e/EssenceFlux.java b/Mage.Sets/src/mage/cards/e/EssenceFlux.java index 2c7a534e996..d96176c17bd 100644 --- a/Mage.Sets/src/mage/cards/e/EssenceFlux.java +++ b/Mage.Sets/src/mage/cards/e/EssenceFlux.java @@ -54,12 +54,11 @@ import mage.util.CardUtil; public class EssenceFlux extends CardImpl { public EssenceFlux(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); // Exile target creature you control, then return that card to the battlefield under its owner's control. If it's a Spirit, put a +1/+1 counter on it. this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); Effect effect = new ExileTargetForSourceEffect(); - effect.setApplyEffectsAfter(); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addEffect(new EssenceFluxEffect()); } @@ -102,8 +101,7 @@ class EssenceFluxEffect extends OneShotEffect { for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { if (exileZone.contains(targetId)) { cardsToBattlefield.add(targetId); - } - else { + } else { Card card = game.getCard(targetId); if (card != null && card instanceof MeldCard) { MeldCard meldCard = (MeldCard) card; diff --git a/Mage.Sets/src/mage/cards/f/FelidarGuardian.java b/Mage.Sets/src/mage/cards/f/FelidarGuardian.java index c7acc317d93..020fb9748c5 100644 --- a/Mage.Sets/src/mage/cards/f/FelidarGuardian.java +++ b/Mage.Sets/src/mage/cards/f/FelidarGuardian.java @@ -64,7 +64,6 @@ public class FelidarGuardian extends CardImpl { // When Felidar Guardian enters the battlefield, you may exile another target permanent you control, then return that card to the battlefield under its owner's control. Effect effect = new ExileTargetForSourceEffect(); - effect.setApplyEffectsAfter(); Ability ability = new EntersBattlefieldTriggeredAbility(effect, true); ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect()); ability.addTarget(new TargetControlledPermanent(filter)); diff --git a/Mage.Sets/src/mage/cards/g/GrabTheReins.java b/Mage.Sets/src/mage/cards/g/GrabTheReins.java index 5782572e465..0a0e42f590d 100644 --- a/Mage.Sets/src/mage/cards/g/GrabTheReins.java +++ b/Mage.Sets/src/mage/cards/g/GrabTheReins.java @@ -51,12 +51,12 @@ import mage.target.common.TargetCreaturePermanent; /** * * @author LoneFox - + * */ public class GrabTheReins extends CardImpl { public GrabTheReins(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}"); // Choose one - this.getSpellAbility().getModes().setMinModes(1); @@ -64,7 +64,6 @@ public class GrabTheReins extends CardImpl { // Until end of turn, you gain control of target creature and it gains haste; Effect effect = new GainControlTargetEffect(Duration.EndOfTurn); effect.setText("Until end of turn, you gain control of target creature"); - effect.setApplyEffectsAfter(); this.getSpellAbility().addEffect(effect); effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); effect.setText("and it gains haste"); @@ -111,15 +110,15 @@ class GrabTheReinsEffect extends OneShotEffect { Target target = new TargetCreaturePermanent(); target.setNotTarget(true); target.setTargetName("a creature to sacrifice"); - if(!target.canChoose(source.getSourceId(), controllerId, game)) { + if (!target.canChoose(source.getSourceId(), controllerId, game)) { return false; } Player player = game.getPlayer(controllerId); - if(player != null) { + if (player != null) { player.chooseTarget(Outcome.Sacrifice, target, source, game); Permanent creatureToSacrifice = game.getPermanent(target.getTargets().get(0)); int amount = creatureToSacrifice.getPower().getValue(); - if(!creatureToSacrifice.sacrifice(creatureToSacrifice.getId(), game)) { + if (!creatureToSacrifice.sacrifice(creatureToSacrifice.getId(), game)) { return false; } if (amount > 0) { @@ -133,8 +132,7 @@ class GrabTheReinsEffect extends OneShotEffect { player.damage(amount, source.getSourceId(), game, false, true); return true; } - } - else { + } else { return true; } } diff --git a/Mage.Sets/src/mage/cards/h/HuntTheHunter.java b/Mage.Sets/src/mage/cards/h/HuntTheHunter.java index 9c35eb5c08d..9a2dc29460f 100644 --- a/Mage.Sets/src/mage/cards/h/HuntTheHunter.java +++ b/Mage.Sets/src/mage/cards/h/HuntTheHunter.java @@ -53,6 +53,7 @@ public class HuntTheHunter extends CardImpl { private static final FilterControlledCreaturePermanent filterControlledGreen = new FilterControlledCreaturePermanent("green creature you control"); private static final FilterCreaturePermanent filterOpponentGreen = new FilterCreaturePermanent("green creature an opponent controls"); + static { filterControlledGreen.add(new ColorPredicate(ObjectColor.GREEN)); filterOpponentGreen.add(new ControllerPredicate(TargetController.OPPONENT)); @@ -60,14 +61,12 @@ public class HuntTheHunter extends CardImpl { } public HuntTheHunter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}"); // Target green creature you control gets +2/+2 until end of turn. It fights target green creature an opponent controls. - Effect effect = new BoostTargetEffect(2,2, Duration.EndOfTurn); - effect.setApplyEffectsAfter(); + Effect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(1,1,filterControlledGreen, false)); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(1, 1, filterControlledGreen, false)); effect = new FightTargetsEffect(); effect.setText("It fights target green creature an opponent controls"); diff --git a/Mage.Sets/src/mage/cards/h/HuntTheWeak.java b/Mage.Sets/src/mage/cards/h/HuntTheWeak.java index 4b026e2b9e6..4670631262e 100644 --- a/Mage.Sets/src/mage/cards/h/HuntTheWeak.java +++ b/Mage.Sets/src/mage/cards/h/HuntTheWeak.java @@ -49,17 +49,16 @@ import mage.target.common.TargetCreaturePermanent; public class HuntTheWeak extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); + static { filter.add(new ControllerPredicate(TargetController.NOT_YOU)); } public HuntTheWeak(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{G}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); // Put a +1/+1 counter on target creature you control. Then that creature fights target creature you don't control. Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); - effect.setApplyEffectsAfter(); this.getSpellAbility().addEffect(effect); effect = new FightTargetsEffect(); effect.setText("Then that creature fights target creature you don't control"); diff --git a/Mage.Sets/src/mage/cards/n/NissasJudgment.java b/Mage.Sets/src/mage/cards/n/NissasJudgment.java index e2c2e3b44cc..f0c9807ad18 100644 --- a/Mage.Sets/src/mage/cards/n/NissasJudgment.java +++ b/Mage.Sets/src/mage/cards/n/NissasJudgment.java @@ -60,11 +60,10 @@ public class NissasJudgment extends CardImpl { } public NissasJudgment(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}"); // Support 2. Effect effect = new SupportEffect(this, 2, false); - effect.setApplyEffectsAfter(); getSpellAbility().addEffect(effect); // Choose up to one target creature an opponent controls. Each creature you control with a +1/+1 counter on it deals damage equal to its power to that creature. diff --git a/Mage.Sets/src/mage/cards/p/PlowThroughReito.java b/Mage.Sets/src/mage/cards/p/PlowThroughReito.java index 1b573a46561..24edcbcd25a 100644 --- a/Mage.Sets/src/mage/cards/p/PlowThroughReito.java +++ b/Mage.Sets/src/mage/cards/p/PlowThroughReito.java @@ -46,14 +46,13 @@ import mage.target.common.TargetCreaturePermanent; public class PlowThroughReito extends CardImpl { public PlowThroughReito(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); this.subtype.add(SubType.ARCANE); - // Sweep - Return any number of Plains you control to their owner's hand. Target creature gets +1/+1 until end of turn for each Plains returned this way. this.getSpellAbility().addEffect(new SweepEffect(SubType.PLAINS)); - DynamicValue sweepValue = new SweepNumber("Plains", true); - this.getSpellAbility().addEffect(new BoostTargetEffect(sweepValue, sweepValue, Duration.EndOfTurn)); + DynamicValue sweepValue = new SweepNumber("Plains"); + this.getSpellAbility().addEffect(new BoostTargetEffect(sweepValue, sweepValue, Duration.EndOfTurn, true)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/s/SavagePunch.java b/Mage.Sets/src/mage/cards/s/SavagePunch.java index 9599a521e56..486b48dcf87 100644 --- a/Mage.Sets/src/mage/cards/s/SavagePunch.java +++ b/Mage.Sets/src/mage/cards/s/SavagePunch.java @@ -45,7 +45,6 @@ import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; - /** * * @author LevelX2 @@ -59,14 +58,13 @@ public class SavagePunch extends CardImpl { } public SavagePunch(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); // Ferocious - The creature you control gets +2/+2 until end of turn before it fights if you control a creature with power 4 or greater. Effect effect = new ConditionalContinuousEffect( - new BoostTargetEffect(2,2,Duration.EndOfTurn), + new BoostTargetEffect(2, 2, Duration.EndOfTurn), new LockedInCondition(FerociousCondition.instance), "Ferocious — The creature you control gets +2/+2 until end of turn before it fights if you control a creature with power 4 or greater"); - effect.setApplyEffectsAfter(); this.getSpellAbility().addEffect(effect); // Target creature you control fights target creature you don't control. diff --git a/Mage.Sets/src/mage/cards/s/SavageStomp.java b/Mage.Sets/src/mage/cards/s/SavageStomp.java index 96c8084d4c5..50890212100 100644 --- a/Mage.Sets/src/mage/cards/s/SavageStomp.java +++ b/Mage.Sets/src/mage/cards/s/SavageStomp.java @@ -75,7 +75,6 @@ public class SavageStomp extends CardImpl { // Put a +1/+1 counter on target creature you control. Then that creature fights target creature you don't control. Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); - effect.setApplyEffectsAfter(); this.getSpellAbility().addEffect(effect); effect = new FightTargetsEffect(); effect.setText("Then that creature fights target creature you don't control"); diff --git a/Mage.Sets/src/mage/cards/s/SinkIntoTakenuma.java b/Mage.Sets/src/mage/cards/s/SinkIntoTakenuma.java index f4691dd9c67..99e5c2edb8d 100644 --- a/Mage.Sets/src/mage/cards/s/SinkIntoTakenuma.java +++ b/Mage.Sets/src/mage/cards/s/SinkIntoTakenuma.java @@ -45,13 +45,12 @@ import mage.target.TargetPlayer; public class SinkIntoTakenuma extends CardImpl { public SinkIntoTakenuma(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}"); this.subtype.add(SubType.ARCANE); - // Sweep - Return any number of Swamps you control to their owner's hand. Target player discards a card for each Swamp returned this way. this.getSpellAbility().addEffect(new SweepEffect(SubType.SWAMP)); - DynamicValue sweepValue = new SweepNumber("Swamp", false); + DynamicValue sweepValue = new SweepNumber("Swamp"); this.getSpellAbility().addEffect(new DiscardTargetEffect(sweepValue)); this.getSpellAbility().addTarget(new TargetPlayer()); } diff --git a/Mage.Sets/src/mage/cards/s/StartYourEngines.java b/Mage.Sets/src/mage/cards/s/StartYourEngines.java index 4cc5a1ad2eb..c4e0b086579 100644 --- a/Mage.Sets/src/mage/cards/s/StartYourEngines.java +++ b/Mage.Sets/src/mage/cards/s/StartYourEngines.java @@ -27,6 +27,7 @@ */ package mage.cards.s; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; @@ -37,8 +38,6 @@ import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; -import java.util.UUID; - /** * * @author LevelX2 @@ -46,11 +45,10 @@ import java.util.UUID; public class StartYourEngines extends CardImpl { public StartYourEngines(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); // Vehicles you control becomes artifact creatures until end of turn. Effect effect = new StartYourEnginesEffect(); - effect.setApplyEffectsAfter(); // needed to recognize vehicle as creatures by the next effect this.getSpellAbility().addEffect(effect); // Creatures you control get +2/+0 until end of turn. diff --git a/Mage.Sets/src/mage/cards/s/SwiftKick.java b/Mage.Sets/src/mage/cards/s/SwiftKick.java index c34d8f1e9f7..f83098c618c 100644 --- a/Mage.Sets/src/mage/cards/s/SwiftKick.java +++ b/Mage.Sets/src/mage/cards/s/SwiftKick.java @@ -49,23 +49,22 @@ import mage.target.common.TargetCreaturePermanent; public class SwiftKick extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); + static { filter.add(new ControllerPredicate(TargetController.NOT_YOU)); } public SwiftKick(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R}"); // Target creature you control gets +1/+0 until end of turn. It fights target creature you don't control. - Effect effect = new BoostTargetEffect(1,0,Duration.EndOfTurn); - effect.setApplyEffectsAfter(); + Effect effect = new BoostTargetEffect(1, 0, Duration.EndOfTurn); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - + effect = new FightTargetsEffect(); effect.setText("It fights target creature you don't control"); - this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addEffect(effect); Target target = new TargetCreaturePermanent(filter); this.getSpellAbility().addTarget(target); diff --git a/Mage.Sets/src/mage/cards/s/SylvanLibrary.java b/Mage.Sets/src/mage/cards/s/SylvanLibrary.java index c0acfd38018..27106b90094 100644 --- a/Mage.Sets/src/mage/cards/s/SylvanLibrary.java +++ b/Mage.Sets/src/mage/cards/s/SylvanLibrary.java @@ -132,7 +132,7 @@ class SylvanLibraryEffect extends OneShotEffect { } } } - controller.putCardsOnTopOfLibrary(cardsPutBack, game, source, applyEffectsAfter); + controller.putCardsOnTopOfLibrary(cardsPutBack, game, source, false); } } return true; diff --git a/Mage.Sets/src/mage/cards/t/TemurCharm.java b/Mage.Sets/src/mage/cards/t/TemurCharm.java index 88a27cfcde4..cf9306775a3 100644 --- a/Mage.Sets/src/mage/cards/t/TemurCharm.java +++ b/Mage.Sets/src/mage/cards/t/TemurCharm.java @@ -57,39 +57,37 @@ public class TemurCharm extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you don't control"); private static final FilterCreaturePermanent filterCantBlock = new FilterCreaturePermanent("Creatures with power 3 or less"); - + static { filter.add(new ControllerPredicate(TargetController.NOT_YOU)); filterCantBlock.add(new PowerPredicate(ComparisonType.FEWER_THAN, 4)); } - - public TemurCharm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{G}{U}{R}"); + public TemurCharm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}{U}{R}"); // Choose one - // Target creature you control gets +1/+1 until end of turn. That creature fights target creature you don't control. - Effect effect = new BoostTargetEffect(1,1,Duration.EndOfTurn); - effect.setApplyEffectsAfter(); + Effect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); this.getSpellAbility().addEffect(effect); effect = new FightTargetsEffect(); effect.setText("That creature fights target creature you don't control"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); Target target = new TargetCreaturePermanent(filter); - this.getSpellAbility().addTarget(target); - + this.getSpellAbility().addTarget(target); + // Counter target spell unless its controller pays {3}. Mode mode = new Mode(); mode.getEffects().add(new CounterUnlessPaysEffect(new GenericManaCost(3))); mode.getTargets().add(new TargetSpell()); this.getSpellAbility().addMode(mode); - + // Creatures with power 3 or less can't block this turn. mode = new Mode(); mode.getEffects().add(new CantBlockAllEffect(filterCantBlock, Duration.EndOfTurn)); this.getSpellAbility().addMode(mode); - + } public TemurCharm(final TemurCharm card) { diff --git a/Mage.Sets/src/mage/cards/w/WildInstincts.java b/Mage.Sets/src/mage/cards/w/WildInstincts.java index 845c74b808e..0dea866d011 100644 --- a/Mage.Sets/src/mage/cards/w/WildInstincts.java +++ b/Mage.Sets/src/mage/cards/w/WildInstincts.java @@ -54,11 +54,10 @@ public class WildInstincts extends CardImpl { } public WildInstincts(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); // Target creature you control gets +2/+2 until end of turn. It fights target creature an opponent controls. Effect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn); - effect.setApplyEffectsAfter(); getSpellAbility().addEffect(effect); getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); effect = new FightTargetsEffect(); diff --git a/Mage.Sets/src/mage/cards/w/WispweaverAngel.java b/Mage.Sets/src/mage/cards/w/WispweaverAngel.java index 21c0c3db4f3..7adfdf9fa97 100644 --- a/Mage.Sets/src/mage/cards/w/WispweaverAngel.java +++ b/Mage.Sets/src/mage/cards/w/WispweaverAngel.java @@ -42,8 +42,8 @@ import mage.cards.Cards; import mage.cards.CardsImpl; import mage.cards.MeldCard; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; @@ -66,7 +66,7 @@ public class WispweaverAngel extends CardImpl { } public WispweaverAngel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}"); this.subtype.add(SubType.ANGEL); this.power = new MageInt(4); this.toughness = new MageInt(4); @@ -76,7 +76,6 @@ public class WispweaverAngel extends CardImpl { // When Wispweaver Angel enters the battlefield, you may exile another target creature you control, then return that card to the battlefield under its owner's control. Effect effect = new ExileTargetForSourceEffect(); - effect.setApplyEffectsAfter(); Ability ability = new EntersBattlefieldTriggeredAbility(effect, true); ability.addTarget(new TargetControlledCreaturePermanent(1, 1, filter, false)); ability.addEffect(new WispweaverAngelEffect()); diff --git a/Mage.Sets/src/mage/cards/z/ZealousConscripts.java b/Mage.Sets/src/mage/cards/z/ZealousConscripts.java index 4b8ef517ddc..ffb48ffb74d 100644 --- a/Mage.Sets/src/mage/cards/z/ZealousConscripts.java +++ b/Mage.Sets/src/mage/cards/z/ZealousConscripts.java @@ -49,7 +49,7 @@ import mage.target.TargetPermanent; public class ZealousConscripts extends CardImpl { public ZealousConscripts(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); this.subtype.add(SubType.HUMAN, SubType.WARRIOR); this.power = new MageInt(3); diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 25528d242b6..0a4d9588731 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -220,10 +220,8 @@ public abstract class AbilityImpl implements Ability { * too late Example: * {@link org.mage.test.cards.replacement.DryadMilitantTest#testDiesByDestroy testDiesByDestroy} */ - if (effect.applyEffectsAfter()) { - game.applyEffects(); - game.getState().getTriggers().checkStateTriggers(game); - } + game.applyEffects(); + game.getState().getTriggers().checkStateTriggers(game); } } return result; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SweepNumber.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SweepNumber.java index 815988f7844..6aebb054ed2 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/SweepNumber.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/SweepNumber.java @@ -30,7 +30,6 @@ package mage.abilities.dynamicvalue.common; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; -import mage.cards.Card; import mage.game.Game; /** @@ -39,37 +38,21 @@ import mage.game.Game; */ public class SweepNumber implements DynamicValue { - private int zoneChangeCounter = 0; private final String sweepSubtype; - private final boolean previousZone; - public SweepNumber(String sweepSubtype, boolean previousZone) { + public SweepNumber(String sweepSubtype) { this.sweepSubtype = sweepSubtype; - this.previousZone = previousZone; } @Override public int calculate(Game game, Ability source, Effect effect) { - if (zoneChangeCounter == 0) { - Card card = game.getCard(source.getSourceId()); - if (card != null) { - zoneChangeCounter = card.getZoneChangeCounter(game); - if (previousZone) { - zoneChangeCounter--; - } - } - } - int number = 0; - Integer sweepNumber = (Integer) game.getState().getValue(new StringBuilder("sweep").append(source.getSourceId()).append(zoneChangeCounter).toString()); - if (sweepNumber != null) { - number = sweepNumber; - } - return number; + Integer sweepNumber = (Integer) game.getState().getValue("sweep" + source.getSourceId() + game.getState().getZoneChangeCounter(source.getSourceId())); + return sweepNumber != null ? sweepNumber : 0; } @Override public SweepNumber copy() { - return new SweepNumber(sweepSubtype, previousZone); + return new SweepNumber(sweepSubtype); } @Override @@ -79,6 +62,6 @@ public class SweepNumber implements DynamicValue { @Override public String getMessage() { - return new StringBuilder("the number of ").append(sweepSubtype).append(sweepSubtype.endsWith("s") ? "":"s").append(" returned this way").toString(); + return "the number of " + sweepSubtype + (sweepSubtype.endsWith("s") ? "" : "s") + " returned this way"; } } diff --git a/Mage/src/main/java/mage/abilities/effects/Effect.java b/Mage/src/main/java/mage/abilities/effects/Effect.java index cc7d51c7b34..2042446cf3b 100644 --- a/Mage/src/main/java/mage/abilities/effects/Effect.java +++ b/Mage/src/main/java/mage/abilities/effects/Effect.java @@ -88,10 +88,6 @@ public interface Effect extends Serializable { Object getValue(String key); - void setApplyEffectsAfter(); - - boolean applyEffectsAfter(); - Effect copy(); } diff --git a/Mage/src/main/java/mage/abilities/effects/EffectImpl.java b/Mage/src/main/java/mage/abilities/effects/EffectImpl.java index ccc7f08f3d8..873111014ea 100644 --- a/Mage/src/main/java/mage/abilities/effects/EffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/EffectImpl.java @@ -138,18 +138,4 @@ public abstract class EffectImpl implements Effect { } return values.get(key); } - - /** - * If set, the game.applyEffects() method will be called to apply the - * effects before the next effect (of the same ability) will resolve. - */ - @Override - public void setApplyEffectsAfter() { - applyEffectsAfter = true; - } - - @Override - public boolean applyEffectsAfter() { - return applyEffectsAfter; - } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java index 7a743e6f5e8..f6059d89d8b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java @@ -37,6 +37,7 @@ 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; import mage.game.permanent.Permanent; @@ -54,15 +55,15 @@ public class BoostControlledEffect extends ContinuousEffectImpl { protected boolean lockedIn = false; public BoostControlledEffect(int power, int toughness, Duration duration) { - this(power, toughness, duration, new FilterCreaturePermanent("creatures"), false); + this(power, toughness, duration, StaticFilters.FILTER_PERMANENT_CREATURES, false); } public BoostControlledEffect(DynamicValue power, DynamicValue toughness, Duration duration) { - this(power, toughness, duration, new FilterCreaturePermanent("creatures"), false); + this(power, toughness, duration, StaticFilters.FILTER_PERMANENT_CREATURES, false); } public BoostControlledEffect(int power, int toughness, Duration duration, boolean excludeSource) { - this(power, toughness, duration, new FilterCreaturePermanent("creatures"), excludeSource); + this(power, toughness, duration, StaticFilters.FILTER_PERMANENT_CREATURES, excludeSource); } public BoostControlledEffect(int power, int toughness, Duration duration, FilterCreaturePermanent filter) { @@ -91,7 +92,7 @@ public class BoostControlledEffect extends ContinuousEffectImpl { super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); this.power = power; this.toughness = toughness; - this.filter = filter; + this.filter = (filter == null ? StaticFilters.FILTER_PERMANENT_CREATURES : filter); this.excludeSource = excludeSource; this.lockedIn = lockedIn; setText(); diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/SweepEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/SweepEffect.java index ba0f02e42b7..b70c5895679 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/SweepEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/SweepEffect.java @@ -70,7 +70,7 @@ public class SweepEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - FilterPermanent filter = new FilterControlledLandPermanent(new StringBuilder("any number of ").append(sweepSubtype).append("s you control").toString()); + FilterPermanent filter = new FilterControlledLandPermanent("any number of " + sweepSubtype + "s you control"); filter.add(new SubtypePredicate(sweepSubtype)); Target target = new TargetPermanent(0, Integer.MAX_VALUE, filter, true); if (controller.chooseTarget(outcome, target, source, game)) { diff --git a/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java b/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java index 7984d2383a2..23a8b876bb1 100644 --- a/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/InspiredAbility.java @@ -1,4 +1,4 @@ - /* +/* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -24,8 +24,7 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. - */ - + */ package mage.abilities.keyword; import mage.abilities.TriggeredAbilityImpl; @@ -41,7 +40,6 @@ import mage.game.events.GameEvent.EventType; * * @author LevelX2 */ - public class InspiredAbility extends TriggeredAbilityImpl { public InspiredAbility(Effect effect) { @@ -73,6 +71,6 @@ public class InspiredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return new StringBuilder("Inspired - Whenever {this} becomes untapped, ").append(super.getRule()).toString(); + return "Inspired - Whenever {this} becomes untapped, " + super.getRule(); } }