diff --git a/.gitignore b/.gitignore index 1e1aa9de62a..5eb4c929aef 100644 --- a/.gitignore +++ b/.gitignore @@ -50,7 +50,8 @@ Mage.Updater/target mage.updater.client/target releases -Utils/author.txt +Utils/author.txt +.DS_Store .metadata .project .settings @@ -88,4 +89,4 @@ Mage.Server.Plugins/Mage.Draft.8PlayerBooster/target *.netbeans_automatic_build *.txt Mage.Client/serverlist.txt -/Mage.Network/target/ \ No newline at end of file +/Mage.Network/target/ diff --git a/Mage.Client/release/sample-decks/Commander/Tasigur BGU.dck b/Mage.Client/release/sample-decks/Commander/Tasigur BGU.dck new file mode 100644 index 00000000000..5c185ee1268 --- /dev/null +++ b/Mage.Client/release/sample-decks/Commander/Tasigur BGU.dck @@ -0,0 +1,97 @@ +1 [MMQ:316] Dust Bowl +1 [MBS:43] Go for the Throat +1 [ALL:42] Force of Will +1 [ZEN:220] Misty Rainforest +1 [10E:361] Treetop Village +1 [10E:362] Underground River +1 [ROE:115] Inquisition of Kozilek +1 [STH:137] Volrath's Stronghold +1 [PLC:85] Damnation +1 [FUT:173] Tolaria West +1 [ISD:78] Snapcaster Mage +1 [ZEN:67] Spell Pierce +1 [ZEN:229] Verdant Catacombs +1 [M11:70] Preordain +1 [C13:177] Baleful Strix +1 [ZEN:223] Scalding Tarn +1 [MBS:138] Sword of Feast and Famine +1 [NPH:57] Dismember +1 [TMP:58] Dismiss +1 [INV:57] Fact or Fiction +1 [DGM:93] Putrefy +1 [ONS:320] Lonely Sandbar +1 [GTC:240] Breeding Pool +1 [DTK:262] Forest +1 [ISD:105] Liliana of the Veil +1 [TMP:340] Wasteland +1 [JUD:46] Mental Note +1 [ULG:36] Miscalculation +2 [CSP:152] Snow-Covered Island +1 [RAV:63] Remand +1 [LRW:56] Cryptic Command +1 [3ED:283] Bayou +1 [5ED:191] Sylvan Library +1 [CMD:269] Command Tower +1 [EXO:35] Forbid +1 [DDO:36] AEtherize +1 [KTK:36] Dig Through Time +1 [RTR:141] Abrupt Decay +1 [SHM:280] Sunken Ruins +1 [DIS:33] Spell Snare +1 [ARB:92] Maelstrom Pulse +1 [KTK:81] Murderous Cut +1 [TSP:48] Ancestral Vision +1 [TMP:22] Diabolic Edict +1 [10E:319] Crucible of Worlds +1 [M12:63] Mana Leak +1 [FUT:52] Logic Knot +1 [CHK:268] Sensei's Divining Top +1 [THS:225] Temple of Deceit +1 [ODY:317] Cephalid Coliseum +1 [WWK:145] Tectonic Edge +1 [EVE:41] Raven's Crime +1 [C13:96] Toxic Deluge +1 [9ED:152] Phyrexian Arena +1 [KTK:248] Windswept Heath +1 [KTK:204] Sultai Charm +1 [KTK:249] Wooded Foothills +1 [M13:223] Drowned Catacomb +1 [RAV:172] Life from the Loam +1 [THS:107] Thoughtseize +1 [MMQ:61] Brainstorm +1 [M15:244] Llanowar Wastes +1 [ISD:55] Forbidden Alchemy +1 [APC:114] Pernicious Deed +1 [RTR:243] Overgrown Tomb +1 [7ED:76] Force Spike +1 [TMP:70] Intuition +1 [3ED:305] Underground Sea +1 [M15:248] Urborg, Tomb of Yawgmoth +1 [AVR:226] Cavern of Souls +1 [ISD:241] Hinterland Harbor +1 [DKA:52] Thought Scour +1 [3ED:303] Tropical Island +1 [3ED:13] Demonic Tutor +1 [ISD:249] Woodland Cemetery +1 [M12:73] Ponder +1 [DTK:65] Negate +1 [RAV:82] Darkblast +1 [WWK:134] Creeping Tar Pit +1 [GTC:249] Watery Grave +1 [WWK:132] Bojuka Bog +1 [KTK:239] Polluted Delta +1 [KTK:233] Flooded Strand +1 [KTK:59] Treasure Cruise +1 [TSP:202] Krosan Grip +1 [7ED:67] Counterspell +1 [MIR:80] Mystical Tutor +1 [MOR:55] Vendilion Clique +1 [TSP:69] Mystical Teachings +1 [KTK:230] Bloodstained Mire +1 [ZEN:219] Marsh Flats +1 [DTK:256] Swamp +1 [THS:90] Hero's Downfall +3 [DTK:253] Island +1 [WWK:31] Jace, the Mind Sculptor +1 [DTK:98] Duress +SB: 1 [FRF:87] Tasigur, the Golden Fang diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 3a9896acf5b..834f3cdcad8 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -93,6 +93,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.prefs.Preferences; +import static mage.client.dialog.PreferencesDialog.KEY_CONNECT_FLAG; import mage.cards.repository.ExpansionInfo; import mage.cards.repository.ExpansionRepository; import mage.client.util.audio.AudioManager; @@ -800,10 +801,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { ProxyType proxyType = ProxyType.valueByText(prefs.get("proxyType", "None")); String proxyUsername = prefs.get("proxyUsername", ""); String proxyPassword = prefs.get("proxyPassword", ""); - int avatarId = PreferencesDialog.getSelectedAvatar(); - boolean showAbilityPickerForced = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_ABILITY_PICKER_FORCED, "true").equals("true"); - - try { setCursor(new Cursor(Cursor.WAIT_CURSOR)); Connection connection = new Connection(); @@ -815,10 +812,9 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { connection.setProxyPort(proxyPort); connection.setProxyUsername(proxyUsername); connection.setProxyPassword(proxyPassword); - connection.setAvatarId(avatarId); - connection.setShowAbilityPickerForced(showAbilityPickerForced); - connection.setAllowRequestShowHandCards(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true").equals("true")); - connection.setUserSkipPrioritySteps(PreferencesDialog.getUserSkipPrioritySteps()); + + setUserPrefsToConnection(connection); + logger.debug("connecting (auto): " + proxyType + " " + proxyServer + " " + proxyPort + " " + proxyUsername); if (connect(connection)) { return true; @@ -831,6 +827,17 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { return false; } + public void setUserPrefsToConnection(Connection connection) { + int avatarId = PreferencesDialog.getSelectedAvatar(); + connection.setAvatarId(avatarId); + boolean showAbilityPickerForced = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_ABILITY_PICKER_FORCED, "true").equals("true"); + connection.setShowAbilityPickerForced(showAbilityPickerForced); + connection.setAllowRequestShowHandCards(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true").equals("true")); + connection.setConfirmEmptyManaPool(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true").equals("true")); + connection.setUserSkipPrioritySteps(PreferencesDialog.getUserSkipPrioritySteps()); + connection.setFlagName(MageFrame.getPreferences().get(KEY_CONNECT_FLAG, "world.png")); + } + /** * This method is called from within the constructor to * initialize the form. diff --git a/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java b/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java index 7d9bc82b50b..0e3e2f88cbd 100644 --- a/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java +++ b/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java @@ -37,14 +37,24 @@ import java.awt.Color; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; +import javax.swing.Icon; +import javax.swing.ImageIcon; import javax.swing.JTextField; import javax.swing.table.AbstractTableModel; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumnModel; import mage.client.MageFrame; +import mage.client.dialog.PreferencesDialog; +import static mage.client.dialog.PreferencesDialog.KEY_TABLES_COLUMNS_ORDER; +import static mage.client.dialog.PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH; +import static mage.client.dialog.PreferencesDialog.KEY_USERS_COLUMNS_ORDER; +import static mage.client.dialog.PreferencesDialog.KEY_USERS_COLUMNS_WIDTH; import mage.client.util.MageTableRowSorter; +import mage.client.util.gui.TableUtil; import mage.client.util.audio.AudioManager; import mage.remote.MageRemoteException; import mage.view.ChatMessage; @@ -53,6 +63,7 @@ import mage.view.ChatMessage.MessageColor; import mage.view.ChatMessage.MessageType; import mage.view.RoomUsersView; import mage.view.UsersView; +import org.apache.log4j.Logger; import org.mage.card.arcane.ManaSymbols; import org.mage.network.Client; @@ -61,7 +72,9 @@ import org.mage.network.Client; * @author BetaSteward_at_googlemail.com, nantuko */ public class ChatPanel extends javax.swing.JPanel { - + + private static final Logger logger = Logger.getLogger(ChatPanel.class); + private UUID chatId; private Client client; private final List players = new ArrayList<>(); @@ -120,6 +133,8 @@ public class ChatPanel extends javax.swing.JPanel { */ private ChatType chatType = ChatType.DEFAULT; + private static final int[] defaultColumnsWidth = {20, 100, 100, 80}; + public enum ChatType { DEFAULT, GAME, TABLES, TOURNAMENT @@ -147,6 +162,9 @@ public class ChatPanel extends javax.swing.JPanel { jTablePlayers.setBackground(new Color(0, 0, 0, ALPHA)); jTablePlayers.setForeground(Color.white); jTablePlayers.setRowSorter(new MageTableRowSorter(tableModel)); + + TableUtil.setColumnWidthAndOrder(jTablePlayers, defaultColumnsWidth, KEY_USERS_COLUMNS_WIDTH, KEY_USERS_COLUMNS_ORDER); + if (jScrollPaneTxt != null) { jScrollPaneTxt.setBackground(new Color(0, 0, 0, ALPHA)); jScrollPaneTxt.getViewport().setBackground(new Color(0, 0, 0, ALPHA)); @@ -160,6 +178,10 @@ public class ChatPanel extends javax.swing.JPanel { } } + public void cleanUp() { + TableUtil.saveColumnWidthAndOrderToPrefs(jTablePlayers, KEY_USERS_COLUMNS_WIDTH, KEY_USERS_COLUMNS_ORDER); + } + public ChatType getChatType() { return chatType; } @@ -367,16 +389,20 @@ public class ChatPanel extends javax.swing.JPanel { class TableModel extends AbstractTableModel { - private final String[] columnNames = new String[]{"Players", "Info", "Games", "Connection"}; + + + private final String[] columnNames = new String[]{" ","Players", "Info", "Games", "Connection"}; private UsersView[] players = new UsersView[0]; + private Map flagIconCache = new HashMap<>(); public void loadData(RoomUsersView roomUserInfo) throws MageRemoteException { // RoomUsersView roomUserInfo = roomUserInfoList.iterator().next(); this.players = roomUserInfo.getUsersView().toArray(new UsersView[0]); JTableHeader th = jTablePlayers.getTableHeader(); TableColumnModel tcm = th.getColumnModel(); - tcm.getColumn(0).setHeaderValue("Players (" + this.players.length + ")"); - tcm.getColumn(2).setHeaderValue( + + tcm.getColumn(1).setHeaderValue("Players (" + this.players.length + ")"); + tcm.getColumn(3).setHeaderValue( "Games " + roomUserInfo.getNumberActiveGames() + (roomUserInfo.getNumberActiveGames() != roomUserInfo.getNumberGameThreads() ? " (T:" + roomUserInfo.getNumberGameThreads():" (") + " limit: " + roomUserInfo.getNumberMaxGames() + ")"); @@ -398,12 +424,14 @@ public class ChatPanel extends javax.swing.JPanel { public Object getValueAt(int arg0, int arg1) { switch (arg1) { case 0: - return players[arg0].getUserName(); + return getCountryFlagIcon(players[arg0].getFlagName()); case 1: - return players[arg0].getInfoState(); + return players[arg0].getUserName(); case 2: - return players[arg0].getInfoGames(); + return players[arg0].getInfoState(); case 3: + return players[arg0].getInfoGames(); + case 4: return players[arg0].getInfoPing(); } return ""; @@ -422,13 +450,32 @@ public class ChatPanel extends javax.swing.JPanel { @Override public Class getColumnClass(int columnIndex) { - return String.class; + switch (columnIndex) { + case 0: + return Icon.class; + default: + return String.class; + } } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } + + private ImageIcon getCountryFlagIcon(String countryCode) { + ImageIcon flagIcon = flagIconCache.get(countryCode); + if (flagIcon == null) { + flagIcon = new javax.swing.ImageIcon(getClass().getResource("/flags/" + countryCode +".png")); + if (flagIcon.getImage() == null) { + logger.warn("Country flag resource not found: " + countryCode); + } else { + flagIconCache.put(countryCode, flagIcon); + } + } + return flagIcon; + } + } public void clear() { diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java index 578a8c80bda..284d183a11c 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -187,6 +187,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { break; case FREE_BUILDING: this.btnSubmit.setVisible(false); + this.btnAddLand.setVisible(true); this.cardSelector.loadCards(this.bigCard); //this.cardTableSelector.loadCards(this.bigCard); this.btnExit.setVisible(true); @@ -797,7 +798,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { private void btnAddLandActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnAddLandActionPerformed AddLandDialog addLand = new AddLandDialog(); - addLand.showDialog(deck); + addLand.showDialog(deck, mode); refreshDeck(); }//GEN-LAST:event_btnAddLandActionPerformed 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 fa5f815e756..c5cfd836728 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.form @@ -29,9 +29,9 @@ - + - + @@ -39,34 +39,43 @@ + - - - - + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + - + - + - + @@ -75,6 +84,11 @@ + + + + + @@ -100,7 +114,7 @@ - + @@ -112,6 +126,23 @@ + + + + + + + + + + + + + + + + + @@ -124,6 +155,11 @@ + + + + + @@ -131,9 +167,16 @@ - + - + + + + + + + + @@ -148,16 +191,9 @@ - + - - - - - - - - + @@ -167,11 +203,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 1c4a9c26973..4210bf4ffb8 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java @@ -31,6 +31,9 @@ import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import javax.swing.DefaultComboBoxModel; import javax.swing.JLayeredPane; import mage.Mana; import mage.cards.Card; @@ -41,6 +44,7 @@ import mage.cards.repository.CardRepository; import mage.cards.repository.ExpansionInfo; import mage.cards.repository.ExpansionRepository; import mage.client.MageFrame; +import mage.client.constants.Constants.DeckEditorMode; import mage.constants.Rarity; /** @@ -60,45 +64,49 @@ public class AddLandDialog extends MageDialog { this.setModal(true); } - public void showDialog(Deck deck) { + public void showDialog(Deck deck, DeckEditorMode mode) { this.deck = deck; - - // decide from which sets basic lands are taken from - for (String setCode :deck.getExpansionSetCodes()) { - ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByCode(setCode); - if (expansionInfo != null && expansionInfo.hasBasicLands()) { - this.setCodesland.add(expansionInfo.getCode()); - } - } - - // if sets have no basic land, take land from block - if (this.setCodesland.isEmpty()) { + SortedSet landSets = new TreeSet<>(); + if (!mode.equals(DeckEditorMode.FREE_BUILDING)) { + // decide from which sets basic lands are taken from for (String setCode :deck.getExpansionSetCodes()) { ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByCode(setCode); - if (expansionInfo != null) { - List blockSets = ExpansionRepository.instance.getSetsFromBlock(expansionInfo.getBlockName()); - for (ExpansionInfo blockSet: blockSets) { - if (blockSet.hasBasicLands()) { - this.setCodesland.add(blockSet.getCode()); + if (expansionInfo != null && expansionInfo.hasBasicLands()) { + this.setCodesland.add(expansionInfo.getCode()); + landSets.add(expansionInfo.getName()); + } + } + + // if sets have no basic land, take land from block + if (this.setCodesland.isEmpty()) { + for (String setCode :deck.getExpansionSetCodes()) { + ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByCode(setCode); + if (expansionInfo != null) { + List blockSets = ExpansionRepository.instance.getSetsFromBlock(expansionInfo.getBlockName()); + for (ExpansionInfo blockSet: blockSets) { + if (blockSet.hasBasicLands()) { + this.setCodesland.add(blockSet.getCode()); + landSets.add(blockSet.getName()); + } } } } } - } - // if still no set with lands found, take one by random + } + // if still no set with lands found, add list of all available if (this.setCodesland.isEmpty()) { - // if sets have no basic lands and also it has no parent or parent has no lands get last set with lands - // select a set with basic lands by random - Random generator = new Random(); List basicLandSets = ExpansionRepository.instance.getSetsWithBasicLandsByReleaseDate(); - if (basicLandSets.size() > 0) { - this.setCodesland.add(basicLandSets.get(generator.nextInt(basicLandSets.size())).getCode()); + for (ExpansionInfo expansionInfo: basicLandSets) { + landSets.add(expansionInfo.getName()); } - } - - if (this.setCodesland.isEmpty()) { + } + if (landSets.isEmpty()) { throw new IllegalArgumentException("No set with basic land was found"); } + if(landSets.size() > 1) { + landSets.add(""); + } + cbLandSet.setModel(new DefaultComboBoxModel(landSets.toArray())); MageFrame.getDesktop().add(this, JLayeredPane.PALETTE_LAYER); this.setVisible(true); @@ -106,14 +114,22 @@ public class AddLandDialog extends MageDialog { private void addLands(String landName, int number) { Random random = new Random(); + String landSetName = (String) cbLandSet.getSelectedItem(); + CardCriteria criteria = new CardCriteria(); - if (!setCodesland.isEmpty()) { + if (landSetName.equals("")) { criteria.setCodes(setCodesland.toArray(new String[setCodesland.size()])); - } + } else { + ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByName(landSetName); + if (expansionInfo == null) { + throw new IllegalArgumentException("Code of Set " + landSetName + " not found"); + } + criteria.setCodes(expansionInfo.getCode()); + } criteria.rarities(Rarity.LAND).name(landName); List cards = CardRepository.instance.findCards(criteria); if (cards.isEmpty()) { - return; + throw new IllegalArgumentException("No basic lands found in Set: " + landSetName); } for (int i = 0; i < number; i++) { @@ -132,16 +148,18 @@ public class AddLandDialog extends MageDialog { private void initComponents() { jButton2 = new javax.swing.JButton(); + lblLandSet = new javax.swing.JLabel(); + cbLandSet = new javax.swing.JComboBox(); lblForest = new javax.swing.JLabel(); spnForest = new javax.swing.JSpinner(); - spnIsland = new javax.swing.JSpinner(); lblIsland = new javax.swing.JLabel(); + spnIsland = new javax.swing.JSpinner(); + lblMountain = new javax.swing.JLabel(); + spnMountain = new javax.swing.JSpinner(); lblPains = new javax.swing.JLabel(); spnPlains = new javax.swing.JSpinner(); - spnMountain = new javax.swing.JSpinner(); - lblMountain = new javax.swing.JLabel(); - spnSwamp = new javax.swing.JSpinner(); lblSwamp = new javax.swing.JLabel(); + spnSwamp = new javax.swing.JSpinner(); btnAdd = new javax.swing.JButton(); btnCancel = new javax.swing.JButton(); btnAutoAdd = new javax.swing.JButton(); @@ -150,26 +168,30 @@ public class AddLandDialog extends MageDialog { setTitle("Add Land"); + lblLandSet.setText("Set"); + + cbLandSet.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + lblForest.setText("Forest"); spnForest.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1))); + lblIsland.setText("Island"); + spnIsland.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1))); - lblIsland.setText("Island"); + lblMountain.setText("Mountain"); + + spnMountain.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1))); lblPains.setText("Plains"); spnPlains.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1))); - spnMountain.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1))); - - lblMountain.setText("Mountain"); + lblSwamp.setText("Swamp"); spnSwamp.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1))); - lblSwamp.setText("Swamp"); - btnAdd.setText("Add"); btnAdd.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -197,40 +219,50 @@ public class AddLandDialog extends MageDialog { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblIsland) .addComponent(lblMountain) - .addComponent(lblForest)) + .addComponent(lblForest) + .addComponent(lblLandSet)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(spnMountain, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) - .addComponent(spnIsland) - .addComponent(spnForest))) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(lblPains) - .addGap(21, 21, 21) - .addComponent(spnPlains)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(lblSwamp) - .addGap(14, 14, 14) - .addComponent(spnSwamp))) - .addGap(114, 114, 114)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(spnMountain, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) + .addComponent(spnIsland) + .addComponent(spnForest)) + .addComponent(cbLandSet, javax.swing.GroupLayout.PREFERRED_SIZE, 207, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addComponent(lblSwamp) + .addGap(14, 14, 14) + .addComponent(spnSwamp)) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addComponent(lblPains) + .addGap(21, 21, 21) + .addComponent(spnPlains))) + .addGap(122, 122, 122))) + .addContainerGap()) .addGroup(layout.createSequentialGroup() .addComponent(btnCancel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(btnAutoAdd) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(btnAdd) - .addGap(0, 40, Short.MAX_VALUE)))) + .addGap(0, 0, Short.MAX_VALUE)))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(cbLandSet, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lblLandSet)) + .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)) @@ -250,7 +282,7 @@ public class AddLandDialog extends MageDialog { .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, 38, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(btnAdd) .addComponent(btnCancel) @@ -322,9 +354,11 @@ public class AddLandDialog extends MageDialog { private javax.swing.JButton btnAdd; private javax.swing.JButton btnAutoAdd; private javax.swing.JButton btnCancel; + private javax.swing.JComboBox cbLandSet; private javax.swing.JButton jButton2; private javax.swing.JLabel lblForest; private javax.swing.JLabel lblIsland; + private javax.swing.JLabel lblLandSet; private javax.swing.JLabel lblMountain; private javax.swing.JLabel lblPains; private javax.swing.JLabel lblSwamp; diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form index 3389484c5d8..e84af7c5e33 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form @@ -35,20 +35,19 @@ - - - - + + + + + + + - - - - - - + + @@ -57,8 +56,9 @@ - + + @@ -86,12 +86,17 @@ + + + + + - + @@ -142,11 +147,24 @@ - + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java index 8c51623d435..c3d5052ac69 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java @@ -37,7 +37,6 @@ package mage.client.dialog; import mage.client.MageFrame; import mage.client.util.Config; import mage.remote.Connection; -import mage.remote.Connection.ProxyType; import org.apache.log4j.Logger; import javax.swing.*; @@ -64,6 +63,10 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static mage.client.dialog.PreferencesDialog.KEY_CONNECT_AUTO_CONNECT; +import static mage.client.dialog.PreferencesDialog.KEY_CONNECT_FLAG; +import mage.client.util.gui.countryBox.CountryItemEditor; +import mage.remote.Connection.ProxyType; /** * @author BetaSteward_at_googlemail.com @@ -96,9 +99,19 @@ public class ConnectDialog extends MageDialog { this.txtServer.setText(MageFrame.getPreferences().get("serverAddress", Config.serverName)); this.txtPort.setText(MageFrame.getPreferences().get("serverPort", Integer.toString(Config.port))); this.txtUserName.setText(MageFrame.getPreferences().get("userName", "")); - this.chkAutoConnect.setSelected(Boolean.parseBoolean(MageFrame.getPreferences().get("autoConnect", "false"))); + this.chkAutoConnect.setSelected(Boolean.parseBoolean(MageFrame.getPreferences().get(KEY_CONNECT_AUTO_CONNECT, "false"))); this.chkForceUpdateDB.setSelected(false); // has always to be set manually to force comparison this.lblStatus.setText(""); + + String selectedFlag = MageFrame.getPreferences().get(KEY_CONNECT_FLAG, "world"); + // set the selected country/flag + for (int i = 0; i < cbFlag.getItemCount(); i++) { + String[] name = (String[])cbFlag.getItemAt(i); + if (name[1].equals(selectedFlag)) { + cbFlag.setSelectedIndex(i); + break; + } + } this.setModal(true); this.setLocation(50, 50); this.setVisible(true); @@ -108,7 +121,7 @@ public class ConnectDialog extends MageDialog { MageFrame.getPreferences().put("serverAddress", txtServer.getText().trim()); MageFrame.getPreferences().put("serverPort", txtPort.getText().trim()); MageFrame.getPreferences().put("userName", txtUserName.getText().trim()); - MageFrame.getPreferences().put("autoConnect", Boolean.toString(chkAutoConnect.isSelected())); + MageFrame.getPreferences().put(KEY_CONNECT_AUTO_CONNECT, Boolean.toString(chkAutoConnect.isSelected())); } /** @@ -128,6 +141,8 @@ public class ConnectDialog extends MageDialog { txtPort = new javax.swing.JTextField(); lblUserName = new javax.swing.JLabel(); txtUserName = new javax.swing.JTextField(); + lblFlag = new javax.swing.JLabel(); + cbFlag = new mage.client.util.gui.countryBox.CountryComboBox(); chkAutoConnect = new javax.swing.JCheckBox(); chkForceUpdateDB = new javax.swing.JCheckBox(); jProxySettingsButton = new javax.swing.JButton(); @@ -160,7 +175,12 @@ public class ConnectDialog extends MageDialog { }); lblUserName.setLabelFor(txtUserName); - lblUserName.setText("User Name:"); + lblUserName.setText("User name:"); + + lblFlag.setLabelFor(txtUserName); + lblFlag.setText("User flag:"); + + cbFlag.setEditable(true); chkAutoConnect.setText("Automatically connect to this server next time"); chkAutoConnect.setToolTipText("If active this connect dialog will not be shown if you choose to connect.
\nInstead XMage tries to connect to the last server you were connected to."); @@ -212,26 +232,26 @@ public class ConnectDialog extends MageDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnCancel)) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lblPort) - .addComponent(lblServer) - .addComponent(lblUserName)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lblPort) + .addComponent(lblServer) + .addComponent(lblUserName)) + .addComponent(lblFlag, javax.swing.GroupLayout.Alignment.TRAILING)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblStatus, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(chkForceUpdateDB, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(chkAutoConnect, javax.swing.GroupLayout.DEFAULT_SIZE, 362, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addComponent(jProxySettingsButton) - .addGap(164, 237, Short.MAX_VALUE)) + .addComponent(jProxySettingsButton) + .addComponent(cbFlag, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addComponent(txtServer, javax.swing.GroupLayout.DEFAULT_SIZE, 286, Short.MAX_VALUE) .addComponent(txtPort, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(txtUserName)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnFind) - .addGap(0, 0, Short.MAX_VALUE))))) + .addComponent(btnFind)) + .addComponent(chkForceUpdateDB, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(chkAutoConnect, javax.swing.GroupLayout.DEFAULT_SIZE, 358, Short.MAX_VALUE)))) .addContainerGap()) ); layout.setVerticalGroup( @@ -251,12 +271,16 @@ public class ConnectDialog extends MageDialog { .addComponent(txtUserName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(lblUserName)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(lblFlag, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(cbFlag, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGap(5, 5, 5) .addComponent(chkAutoConnect) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(chkForceUpdateDB) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jProxySettingsButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 20, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 50, Short.MAX_VALUE) .addComponent(lblStatus, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) @@ -270,6 +294,7 @@ public class ConnectDialog extends MageDialog { private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed MageFrame.getPreferences().put("autoConnect", Boolean.toString(chkAutoConnect.isSelected())); + MageFrame.getPreferences().put(KEY_CONNECT_FLAG, ((CountryItemEditor)cbFlag.getEditor()).getImageItem()); if (task != null && !task.isDone()) { task.cancel(true); } else { @@ -305,7 +330,7 @@ public class ConnectDialog extends MageDialog { connection.setPort(Integer.valueOf(this.txtPort.getText().trim())); connection.setUsername(this.txtUserName.getText().trim()); connection.setForceDBComparison(this.chkForceUpdateDB.isSelected()); - + MageFrame.getPreferences().put(KEY_CONNECT_FLAG, ((CountryItemEditor)cbFlag.getEditor()).getImageItem()); ProxyType configProxyType = Connection.ProxyType.valueByText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_TYPE, "None")); @@ -330,12 +355,8 @@ public class ConnectDialog extends MageDialog { } // pref settings - int avatarId = PreferencesDialog.getSelectedAvatar(); - connection.setAvatarId(avatarId); - boolean showAbilityPickerForced = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_ABILITY_PICKER_FORCED, "true").equals("true"); - connection.setAllowRequestShowHandCards(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true").equals("true")); - connection.setShowAbilityPickerForced(showAbilityPickerForced); - connection.setUserSkipPrioritySteps(PreferencesDialog.getUserSkipPrioritySteps()); + MageFrame.getInstance().setUserPrefsToConnection(connection); + logger.debug("connecting: " + connection.getProxyType() + " " + connection.getProxyHost() + " " + connection.getProxyPort()); task = new ConnectTask(); task.execute(); @@ -525,9 +546,11 @@ public class ConnectDialog extends MageDialog { private javax.swing.JButton btnCancel; private javax.swing.JButton btnConnect; private javax.swing.JButton btnFind; + private mage.client.util.gui.countryBox.CountryComboBox cbFlag; private javax.swing.JCheckBox chkAutoConnect; private javax.swing.JCheckBox chkForceUpdateDB; private javax.swing.JButton jProxySettingsButton; + private javax.swing.JLabel lblFlag; private javax.swing.JLabel lblPort; private javax.swing.JLabel lblServer; private javax.swing.JLabel lblStatus; diff --git a/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java index 871d6f99a41..04c19c82591 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java @@ -52,7 +52,7 @@ public class JoinTableDialog extends MageDialog { public JoinTableDialog() { initComponents(); newPlayerPanel.showLevel(false); - txtPassword.setText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD, "")); + txtPassword.setText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD_JOIN, "")); } public void showDialog(UUID roomId, UUID tableId, boolean isTournament, boolean isLimited) { @@ -149,7 +149,7 @@ public class JoinTableDialog extends MageDialog { private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed Client client = MageFrame.getClient(); try { - PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD, txtPassword.getText()); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD_JOIN, txtPassword.getText()); if (isTournament) { joined = client.joinTournamentTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), "Human", 1, DeckImporterUtil.importDeck(this.newPlayerPanel.getDeckFile()), this.txtPassword.getText()); } else { diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form index 20d3cfa718b..01912da458f 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form @@ -32,20 +32,22 @@ - - - + + + + + - + - - - - + + + + - + @@ -89,10 +91,10 @@ - + - - + + @@ -105,7 +107,7 @@ - + @@ -133,6 +135,7 @@ + @@ -147,11 +150,13 @@ - - - + + + + + @@ -171,8 +176,8 @@ - - + + @@ -185,7 +190,7 @@ - + @@ -237,6 +242,12 @@ + + + + + + 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 d98e94a693d..c047c431084 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -102,6 +102,7 @@ public class NewTableDialog extends MageDialog { cbTimeLimit = new javax.swing.JComboBox(); lblGameType = new javax.swing.JLabel(); cbGameType = new javax.swing.JComboBox(); + chkRollbackTurnsAllowed = new javax.swing.JCheckBox(); lblFreeMulligans = new javax.swing.JLabel(); spnFreeMulligans = new javax.swing.JSpinner(); lblNumPlayers = new javax.swing.JLabel(); @@ -145,6 +146,9 @@ public class NewTableDialog extends MageDialog { } }); + chkRollbackTurnsAllowed.setText("Allow rollbacks"); + chkRollbackTurnsAllowed.setToolTipText("Allow to rollback to the start of previous turns
\nif all players agree.\n"); + lblFreeMulligans.setText("Free Mulligans:"); lblFreeMulligans.setToolTipText("The number of mulligans a player can use without decreasing the number of drawn cards."); @@ -217,18 +221,20 @@ public class NewTableDialog extends MageDialog { .addComponent(lbDeckType) .addComponent(lblGameType)) .addGap(6, 6, 6) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addGroup(layout.createSequentialGroup() - .addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, 398, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) + .addComponent(chkRollbackTurnsAllowed) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 13, Short.MAX_VALUE) .addComponent(lblFreeMulligans) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(txtName, javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(cbDeckType, javax.swing.GroupLayout.Alignment.LEADING, 0, 338, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(txtName) + .addComponent(cbDeckType, 0, 332, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lbTimeLimit, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(lblPassword, javax.swing.GroupLayout.Alignment.TRAILING)) @@ -261,10 +267,10 @@ public class NewTableDialog extends MageDialog { .addComponent(cbAttackOption, javax.swing.GroupLayout.PREFERRED_SIZE, 177, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cbSkillLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 148, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(18, 18, 18) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lblNumWins))) + .addComponent(lblNumWins) + .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE))) .addComponent(jSeparator2) .addComponent(player1Panel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) @@ -273,7 +279,7 @@ public class NewTableDialog extends MageDialog { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(jSeparator3, javax.swing.GroupLayout.DEFAULT_SIZE, 606, Short.MAX_VALUE) + .addComponent(jSeparator3, javax.swing.GroupLayout.DEFAULT_SIZE, 586, Short.MAX_VALUE) .addContainerGap())) ); layout.setVerticalGroup( @@ -295,7 +301,8 @@ public class NewTableDialog extends MageDialog { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lblFreeMulligans)) + .addComponent(lblFreeMulligans) + .addComponent(chkRollbackTurnsAllowed)) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(lblGameType))) @@ -306,11 +313,12 @@ public class NewTableDialog extends MageDialog { .addGap(0, 0, 0) .addComponent(spnNumPlayers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblRange) - .addComponent(lblAttack) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblSkillLevel) - .addComponent(lblNumWins)) + .addComponent(lblNumWins) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblRange) + .addComponent(lblAttack))) .addGap(0, 0, 0) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(cbRange, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -326,8 +334,8 @@ public class NewTableDialog extends MageDialog { .addGap(16, 16, 16) .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 113, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 13, Short.MAX_VALUE) + .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 105, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 7, Short.MAX_VALUE) .addComponent(jSeparator1, 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) @@ -338,7 +346,7 @@ public class NewTableDialog extends MageDialog { .addGroup(layout.createSequentialGroup() .addGap(201, 201, 201) .addComponent(jSeparator3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(178, Short.MAX_VALUE))) + .addContainerGap(167, Short.MAX_VALUE))) ); pack(); @@ -364,6 +372,7 @@ public class NewTableDialog extends MageDialog { options.setSkillLevel((SkillLevel) this.cbSkillLevel.getSelectedItem()); options.setRange((RangeOfInfluence) this.cbRange.getSelectedItem()); options.setWinsNeeded((Integer)this.spnNumWins.getValue()); + options.setRollbackTurnsAllowed(chkRollbackTurnsAllowed.isSelected()); options.setFreeMulligans((Integer)this.spnFreeMulligans.getValue()); options.setPassword(this.txtPassword.getText()); if (!checkMatchOptions(options)) { @@ -598,6 +607,9 @@ public class NewTableDialog extends MageDialog { this.player1Panel.setDeckFile(deckFile); } this.spnNumWins.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_WINS, "2"))); + this.chkRollbackTurnsAllowed.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_ROLLBACK_TURNS_ALLOWED, "Yes").equals("Yes")); + + int range = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_RANGE, "1")); for (RangeOfInfluence roi :RangeOfInfluence.values()) { if (roi.getRange() == range) { @@ -634,6 +646,8 @@ public class NewTableDialog extends MageDialog { PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_TIME_LIMIT, Integer.toString(options.getPriorityTime())); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_GAME_TYPE, options.getGameType()); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_WINS, Integer.toString(options.getWinsNeeded())); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_ROLLBACK_TURNS_ALLOWED, options.isRollbackTurnsAllowed() ? "Yes": "No"); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS, Integer.toString(options.getFreeMulligans())); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_DECK_FILE, deckFile); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_PLAYERS, spnNumPlayers.getValue().toString()); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_RANGE, Integer.toString(options.getRange().getRange())); @@ -660,6 +674,7 @@ public class NewTableDialog extends MageDialog { private javax.swing.JComboBox cbRange; private javax.swing.JComboBox cbSkillLevel; private javax.swing.JComboBox cbTimeLimit; + private javax.swing.JCheckBox chkRollbackTurnsAllowed; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JSeparator jSeparator1; diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.form b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.form index 5736b534446..9e201ee6a22 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.form @@ -51,7 +51,11 @@
- + + + + + @@ -67,52 +71,49 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + @@ -183,10 +184,11 @@ + - + @@ -440,6 +442,12 @@
+ + + + + + @@ -462,7 +470,7 @@ - + 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 475164206b9..18b97f936c6 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java @@ -164,6 +164,7 @@ public class NewTournamentDialog extends MageDialog { cbAllowSpectators = new javax.swing.JCheckBox(); lblPlayer1 = new javax.swing.JLabel(); lblConstructionTime = new javax.swing.JLabel(); + chkRollbackTurnsAllowed = new javax.swing.JCheckBox(); spnConstructTime = new javax.swing.JSpinner(); player1Panel = new mage.client.table.NewPlayerPanel(); pnlPlayers = new javax.swing.JPanel(); @@ -297,6 +298,9 @@ public class NewTournamentDialog extends MageDialog { lblConstructionTime.setText("Construction Time (Minutes):"); + chkRollbackTurnsAllowed.setText("Allow rollbacks"); + chkRollbackTurnsAllowed.setToolTipText("Allow to rollback to the start of previous turns
if all players agree. "); + spnConstructTime.setToolTipText("The time players have to build their deck."); player1Panel.setPreferredSize(new java.awt.Dimension(400, 44)); @@ -312,7 +316,7 @@ public class NewTournamentDialog extends MageDialog { ); pnlPlayersLayout.setVerticalGroup( pnlPlayersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 5, Short.MAX_VALUE) + .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 7, Short.MAX_VALUE) ); btnOk.setText("OK"); @@ -357,7 +361,10 @@ public class NewTournamentDialog extends MageDialog { .addComponent(lblConstructionTime))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(spnConstructTime, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(spnConstructTime, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(chkRollbackTurnsAllowed)) .addGroup(layout.createSequentialGroup() .addComponent(spnNumRounds, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -369,46 +376,44 @@ public class NewTournamentDialog extends MageDialog { .addComponent(btnOk) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnCancel)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lblDraftCube) - .addComponent(lblTournamentType) - .addComponent(lbDeckType) - .addComponent(lblGameType)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(cbDraftCube, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(cbDeckType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createSequentialGroup() - .addComponent(cbTournamentType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lblFreeMulligans) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lblNumWins) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)))) - .addGroup(layout.createSequentialGroup() - .addComponent(lblName) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(txtName, javax.swing.GroupLayout.PREFERRED_SIZE, 124, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lbTimeLimit) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cbTimeLimit, javax.swing.GroupLayout.PREFERRED_SIZE, 89, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lbSkillLevel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cbSkillLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 112, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lblPassword) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(txtPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGap(0, 0, 0)))) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lblDraftCube) + .addComponent(lblTournamentType) + .addComponent(lbDeckType) + .addComponent(lblGameType)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(cbDraftCube, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(cbDeckType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(cbTournamentType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblFreeMulligans) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblNumWins) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addGroup(layout.createSequentialGroup() + .addComponent(lblName) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(txtName, javax.swing.GroupLayout.PREFERRED_SIZE, 124, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lbTimeLimit) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cbTimeLimit, javax.swing.GroupLayout.PREFERRED_SIZE, 101, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(lbSkillLevel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(cbSkillLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblPassword) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(txtPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE))))) .addComponent(player1Panel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) ); @@ -464,9 +469,10 @@ public class NewTournamentDialog extends MageDialog { .addComponent(lblPlayer1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(spnConstructTime, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lblConstructionTime))) + .addComponent(lblConstructionTime) + .addComponent(chkRollbackTurnsAllowed))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(player1Panel, javax.swing.GroupLayout.DEFAULT_SIZE, 62, Short.MAX_VALUE) + .addComponent(player1Panel, javax.swing.GroupLayout.DEFAULT_SIZE, 64, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(pnlPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -535,6 +541,7 @@ public class NewTournamentDialog extends MageDialog { tOptions.getMatchOptions().setFreeMulligans((Integer)this.spnFreeMulligans.getValue()); tOptions.getMatchOptions().setAttackOption(MultiplayerAttackOption.LEFT); tOptions.getMatchOptions().setRange(RangeOfInfluence.ALL); + tOptions.getMatchOptions().setRollbackTurnsAllowed(this.chkRollbackTurnsAllowed.isSelected()); saveTournamentSettingsToPrefs(tOptions); table = client.createTournamentTable(roomId, tOptions); @@ -834,6 +841,7 @@ public class NewTournamentDialog extends MageDialog { } } this.cbAllowSpectators.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_ALLOW_SPECTATORS, "Yes").equals("Yes")); + this.chkRollbackTurnsAllowed.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_ALLOW_ROLLBACKS, "Yes").equals("Yes")); } private void loadBoosterPacks(String packString) { @@ -896,6 +904,7 @@ public class NewTournamentDialog extends MageDialog { } } PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_ALLOW_SPECTATORS, (tOptions.isWatchingAllowed()?"Yes":"No")); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_ALLOW_ROLLBACKS, (tOptions.getMatchOptions().isRollbackTurnsAllowed()?"Yes":"No")); } @@ -915,6 +924,7 @@ public class NewTournamentDialog extends MageDialog { private javax.swing.JComboBox cbSkillLevel; private javax.swing.JComboBox cbTimeLimit; private javax.swing.JComboBox cbTournamentType; + private javax.swing.JCheckBox chkRollbackTurnsAllowed; private javax.swing.JLabel jLabel6; private javax.swing.JLabel lbDeckType; private javax.swing.JLabel lbSkillLevel; 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 024fe542240..06253718466 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form @@ -85,7 +85,7 @@ - + @@ -203,16 +203,17 @@ - + - + + @@ -232,6 +233,8 @@ + + @@ -293,6 +296,17 @@ + + + + + + + + + + +
@@ -1128,7 +1142,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 96d9776e547..6e676093738 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -85,6 +85,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_SHOW_ABILITY_PICKER_FORCED = "showAbilityPicker"; public static final String KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS = "gameAllowRequestShowHandCards"; public static final String KEY_GAME_SHOW_STORM_COUNTER = "gameShowStormCounter"; + public static final String KEY_GAME_CONFIRM_EMPTY_MANA_POOL = "gameConfirmEmptyManaPool"; public static final String KEY_GAME_LOG_AUTO_SAVE = "gameLogAutoSave"; @@ -132,6 +133,10 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_STOP_ALL_MAIN_PHASES = "stopOnAllMainPhases"; public static final String KEY_STOP_ALL_END_PHASES = "stopOnAllEndPhases"; + // mana auto payment + public static final String KEY_GAME_MANA_AUTOPAYMENT = "gameManaAutopayment"; + public static final String KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE = "gameManaAutopaymentOnlyOne"; + // Size of frame to check if divider locations should be used public static final String KEY_MAGE_PANEL_LAST_SIZE = "gamepanelLastSize"; @@ -145,6 +150,10 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_TABLES_DIVIDER_LOCATION_2 = "tablePanelDividerLocation2"; public static final String KEY_TABLES_DIVIDER_LOCATION_3 = "tablePanelDividerLocation3"; + // user list + public static final String KEY_USERS_COLUMNS_WIDTH = "userPanelColumnWidth"; + public static final String KEY_USERS_COLUMNS_ORDER = "userPanelColumnSort"; + public static final String KEY_GAMEPANEL_DIVIDER_LOCATION_0 = "gamepanelDividerLocation0"; public static final String KEY_GAMEPANEL_DIVIDER_LOCATION_1 = "gamepanelDividerLocation1"; public static final String KEY_GAMEPANEL_DIVIDER_LOCATION_2 = "gamepanelDividerLocation2"; @@ -155,10 +164,12 @@ public class PreferencesDialog extends javax.swing.JDialog { // pref setting for new table dialog public static final String KEY_NEW_TABLE_NAME = "newTableName"; public static final String KEY_NEW_TABLE_PASSWORD = "newTablePassword"; + public static final String KEY_NEW_TABLE_PASSWORD_JOIN = "newTablePasswordJoin"; public static final String KEY_NEW_TABLE_DECK_TYPE = "newTableDeckType"; public static final String KEY_NEW_TABLE_TIME_LIMIT = "newTableTimeLimit"; public static final String KEY_NEW_TABLE_GAME_TYPE = "newTableGameType"; public static final String KEY_NEW_TABLE_NUMBER_OF_WINS = "newTableNumberOfWins"; + public static final String KEY_NEW_TABLE_ROLLBACK_TURNS_ALLOWED = "newTableRollbackTurnsAllowed"; public static final String KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS = "newTableNumberOfFreeMulligans"; public static final String KEY_NEW_TABLE_DECK_FILE = "newTableDeckFile"; public static final String KEY_NEW_TABLE_RANGE = "newTableRange"; @@ -181,6 +192,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_NEW_TOURNAMENT_PLAYERS_DRAFT = "newTournamentPlayersDraft"; public static final String KEY_NEW_TOURNAMENT_DRAFT_TIMING = "newTournamentDraftTiming"; public static final String KEY_NEW_TOURNAMENT_ALLOW_SPECTATORS = "newTournamentAllowSpectators"; + public static final String KEY_NEW_TOURNAMENT_ALLOW_ROLLBACKS = "newTournamentAllowRollbacks"; public static final String KEY_NEW_TOURNAMENT_DECK_FILE = "newTournamentDeckFile"; // pref setting for deck generator @@ -219,6 +231,10 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_CONNECTION_URL_SERVER_LIST = "connectionURLServerList"; public static final String KEY_AVATAR = "selectedId"; + + public static final String KEY_CONNECT_AUTO_CONNECT = "autoConnect"; + public static final String KEY_CONNECT_FLAG = "connectFlag"; + private static final Map cache = new HashMap<>(); @@ -329,6 +345,7 @@ public class PreferencesDialog extends javax.swing.JDialog { showAbilityPickerForced = new javax.swing.JCheckBox(); cbAllowRequestToShowHandCards = new javax.swing.JCheckBox(); cbShowStormCounter = new javax.swing.JCheckBox(); + cbConfirmEmptyManaPool = new javax.swing.JCheckBox(); main_gamelog = new javax.swing.JPanel(); cbGameLogAutoSave = new javax.swing.JCheckBox(); tabPhases = new javax.swing.JPanel(); @@ -543,6 +560,16 @@ public class PreferencesDialog extends javax.swing.JDialog { } }); + cbConfirmEmptyManaPool.setSelected(true); + cbConfirmEmptyManaPool.setText("Confirm if you want to pass a phase/step but there is still mana in your mana pool"); + cbConfirmEmptyManaPool.setToolTipText("If activated you get a confirm message if you pass priority while stack is empty
\n and you still have mana in your mana pool."); + cbConfirmEmptyManaPool.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + cbConfirmEmptyManaPool.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cbConfirmEmptyManaPoolActionPerformed(evt); + } + }); + javax.swing.GroupLayout main_gameLayout = new javax.swing.GroupLayout(main_game); main_game.setLayout(main_gameLayout); main_gameLayout.setHorizontalGroup( @@ -552,14 +579,15 @@ public class PreferencesDialog extends javax.swing.JDialog { .addGroup(main_gameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(main_gameLayout.createSequentialGroup() .addComponent(cbAllowRequestToShowHandCards, javax.swing.GroupLayout.PREFERRED_SIZE, 546, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(14, Short.MAX_VALUE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGroup(main_gameLayout.createSequentialGroup() .addGroup(main_gameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(main_gameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addComponent(showPlayerNamesPermanently, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(nonLandPermanentsInOnePile, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(showAbilityPickerForced, javax.swing.GroupLayout.Alignment.LEADING)) - .addComponent(cbShowStormCounter, javax.swing.GroupLayout.PREFERRED_SIZE, 546, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(cbShowStormCounter, javax.swing.GroupLayout.PREFERRED_SIZE, 546, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(cbConfirmEmptyManaPool, javax.swing.GroupLayout.PREFERRED_SIZE, 546, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGap(0, 0, Short.MAX_VALUE)))) ); main_gameLayout.setVerticalGroup( @@ -573,7 +601,9 @@ public class PreferencesDialog extends javax.swing.JDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cbAllowRequestToShowHandCards) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cbShowStormCounter)) + .addComponent(cbShowStormCounter) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cbConfirmEmptyManaPool)) ); nonLandPermanentsInOnePile.getAccessibleContext().setAccessibleName("nonLandPermanentsInOnePile"); @@ -624,7 +654,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .addComponent(main_game, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(main_gamelog, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(63, Short.MAX_VALUE)) + .addContainerGap(40, Short.MAX_VALUE)) ); main_card.getAccessibleContext().setAccessibleName("Game panel"); @@ -1377,7 +1407,7 @@ public class PreferencesDialog extends javax.swing.JDialog { tabAvatars.setLayout(tabAvatarsLayout); tabAvatarsLayout.setHorizontalGroup( tabAvatarsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 598, Short.MAX_VALUE) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 590, Short.MAX_VALUE) ); tabAvatarsLayout.setVerticalGroup( tabAvatarsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -1596,7 +1626,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(exitButton, javax.swing.GroupLayout.PREFERRED_SIZE, 55, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) - .addComponent(tabsPanel) + .addComponent(tabsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 595, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -1624,6 +1654,7 @@ public class PreferencesDialog extends javax.swing.JDialog { save(prefs, dialog.showAbilityPickerForced, KEY_SHOW_ABILITY_PICKER_FORCED, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbAllowRequestToShowHandCards, KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbShowStormCounter, KEY_GAME_SHOW_STORM_COUNTER, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.cbConfirmEmptyManaPool, KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbGameLogAutoSave, KEY_GAME_LOG_AUTO_SAVE, "true", "false", UPDATE_CACHE_POLICY); // Phases @@ -1690,7 +1721,9 @@ public class PreferencesDialog extends javax.swing.JDialog { getSelectedAvatar(), dialog.showAbilityPickerForced.isSelected(), dialog.cbAllowRequestToShowHandCards.isSelected(), - getUserSkipPrioritySteps()); + dialog.cbConfirmEmptyManaPool.isSelected(), + getUserSkipPrioritySteps(), + MageFrame.getPreferences().get(KEY_CONNECT_FLAG, "world.png")); prefs.flush(); } catch (BackingStoreException ex) { @@ -1941,6 +1974,10 @@ public class PreferencesDialog extends javax.swing.JDialog { // TODO add your handling code here: }//GEN-LAST:event_cbShowStormCounterActionPerformed + private void cbConfirmEmptyManaPoolActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbConfirmEmptyManaPoolActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_cbConfirmEmptyManaPoolActionPerformed + private void showProxySettings() { if (cbProxyType.getSelectedItem() == Connection.ProxyType.SOCKS) { this.pnlProxy.setVisible(true); @@ -2017,6 +2054,7 @@ public class PreferencesDialog extends javax.swing.JDialog { load(prefs, dialog.showAbilityPickerForced, KEY_SHOW_ABILITY_PICKER_FORCED, "true"); load(prefs, dialog.cbAllowRequestToShowHandCards, KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true"); load(prefs, dialog.cbShowStormCounter, KEY_GAME_SHOW_STORM_COUNTER, "true"); + load(prefs, dialog.cbConfirmEmptyManaPool, KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true"); load(prefs, dialog.cbGameLogAutoSave, KEY_GAME_LOG_AUTO_SAVE, "true"); @@ -2361,7 +2399,10 @@ public class PreferencesDialog extends javax.swing.JDialog { id, PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_TOOLTIPS_ANY_ZONE, "true").equals("true"), PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true").equals("true"), - getUserSkipPrioritySteps()); + PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true").equals("true"), + getUserSkipPrioritySteps(), + MageFrame.getPreferences().get(KEY_CONNECT_FLAG, "world.png") + ); } } }); @@ -2375,6 +2416,7 @@ public class PreferencesDialog extends javax.swing.JDialog { private javax.swing.JButton btnBrowseImageLocation; private javax.swing.JCheckBox cbAllowRequestToShowHandCards; private javax.swing.JCheckBox cbCheckForNewImages; + private javax.swing.JCheckBox cbConfirmEmptyManaPool; private javax.swing.JCheckBox cbEnableBattlefieldBGM; private javax.swing.JCheckBox cbEnableDraftSounds; private javax.swing.JCheckBox cbEnableGameSounds; diff --git a/Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java b/Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java index ce8e0661026..743bdeffa04 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java @@ -178,12 +178,8 @@ public class UserRequestDialog extends MageDialog { private void sendUserReplay(PlayerAction playerAction) { Client client = MageFrame.getClient(); - switch(playerAction) { - case ADD_PERMISSION_TO_SEE_HAND_CARDS: - client.sendPlayerAction(playerAction, userRequestMessage.getGameId(), userRequestMessage.getRelatedUserId()); - break; - default: - // not supported action + if (session != null && playerAction != null) { + session.sendPlayerAction(playerAction, userRequestMessage.getGameId(), userRequestMessage.getRelatedUserId()); } } diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 8c3dbdca93d..eb46bf74c75 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -90,6 +90,8 @@ import mage.client.dialog.PickChoiceDialog; import mage.client.dialog.PickNumberDialog; import mage.client.dialog.PickPileDialog; import mage.client.dialog.PreferencesDialog; +import static mage.client.dialog.PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT; +import static mage.client.dialog.PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE; import mage.client.dialog.ShowCardsDialog; import mage.client.game.FeedbackPanel.FeedbackMode; import mage.client.plugins.adapters.MageActionCallback; @@ -383,7 +385,8 @@ public final class GamePanel extends javax.swing.JPanel { this.gameChatPanel.connect(client.getGameChatId(gameId)); if (!client.joinGame(gameId)) { removeGame(); - } else { + } else { + // play start sound AudioManager.playYourGameStarted(); } } @@ -457,6 +460,11 @@ public final class GamePanel extends javax.swing.JPanel { public synchronized void init(GameView game) { addPlayers(game); + // default menu states + setMenuStates( + PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT, "true").equals("true"), + PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE, "true").equals("true")); + updateGame(game); } @@ -479,7 +487,8 @@ public final class GamePanel extends javax.swing.JPanel { } } PlayerView player = game.getPlayers().get(playerSeat); - PlayAreaPanel sessionPlayer = new PlayAreaPanel(player, bigCard, gameId, true, game.getPriorityTime(), game.isPlayer(), this); + PlayAreaPanel sessionPlayer = new PlayAreaPanel(player, bigCard, gameId, game.getPriorityTime(), this, + new PlayAreaPanelOptions(game.isPlayer(), true, game.isRollbackTurnsAllowed())); players.put(player.getPlayerId(), sessionPlayer); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; @@ -511,7 +520,8 @@ public final class GamePanel extends javax.swing.JPanel { col = numColumns - 1; } player = game.getPlayers().get(playerNum); - PlayAreaPanel playerPanel = new PlayAreaPanel(player, bigCard, gameId, false, game.getPriorityTime(), game.isPlayer(), this); + PlayAreaPanel playerPanel = new PlayAreaPanel(player, bigCard, gameId, game.getPriorityTime(), this, + new PlayAreaPanelOptions(game.isPlayer(), false, game.isRollbackTurnsAllowed())); players.put(player.getPlayerId(), playerPanel); c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; @@ -728,10 +738,11 @@ public final class GamePanel extends javax.swing.JPanel { /** * Set the same state for menu selections to all player areas. * @param manaPoolAutomatic + * @param manaPoolAutomaticRestricted */ - public void setMenuStates(boolean manaPoolAutomatic) { + public void setMenuStates(boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted) { for(PlayAreaPanel playAreaPanel: players.values()) { - playAreaPanel.setMenuStates(manaPoolAutomatic); + playAreaPanel.setMenuStates(manaPoolAutomatic, manaPoolAutomaticRestricted); } } @@ -802,7 +813,7 @@ public final class GamePanel extends javax.swing.JPanel { ShowCardsDialog newReveal = new ShowCardsDialog(); revealed.put(reveal.getName(), newReveal); } - revealed.get(reveal.getName()).loadCards("Revealed " + reveal.getName(), CardsViewUtil.convertSimple(reveal.getCards(), loadedCards), bigCard, Config.dimensions, gameId, false); + revealed.get(reveal.getName()).loadCards("Revealed " + reveal.getName(), reveal.getCards(), bigCard, Config.dimensions, gameId, false); } } @@ -815,7 +826,7 @@ public final class GamePanel extends javax.swing.JPanel { ShowCardsDialog newLookedAt = new ShowCardsDialog(); lookedAt.put(looked.getName(), newLookedAt); } - lookedAt.get(looked.getName()).loadCards("Looked at by " + looked.getName(), CardsViewUtil.convertSimple(looked.getCards(), loadedCards), bigCard, Config.dimensions, gameId, false); + lookedAt.get(looked.getName()).loadCards("Looked at by " + looked.getName(), looked.getCards(), bigCard, Config.dimensions, gameId, false); } } diff --git a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java index b2e2a1f2860..5f325d1f98b 100644 --- a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java @@ -221,12 +221,18 @@ public class HelperPanel extends JPanel { public void setMessage(String message) { if (message.startsWith("Use alternative cost")) { - textArea.setText("Use alternative cost?"); - } else if (message.length() > 40 && message.contains("Use ")) { - textArea.setText("Use ability?"); - } else { - textArea.setText(message); - } + message = "Use alternative cost?"; + } else if (message.contains("Use ")) { + if (message.length() < this.getWidth() / 10) { + message = getSmallText(message); + } else { + message = "Use ability?" + getSmallText(message.substring(this.getWidth() / 10)); + } + } + textArea.setText(message); + } + protected String getSmallText(String text) { + return "
" + text + "
"; } @Override diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java index e82c03595e2..71d5136aef7 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java @@ -42,6 +42,7 @@ import javax.swing.BorderFactory; import javax.swing.GroupLayout; import javax.swing.GroupLayout.Alignment; import javax.swing.JCheckBoxMenuItem; +import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; @@ -53,6 +54,8 @@ import mage.client.MageFrame; import mage.client.cards.BigCard; import mage.client.dialog.PreferencesDialog; import static mage.client.dialog.PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS; +import static mage.client.dialog.PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT; +import static mage.client.dialog.PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE; import mage.constants.PlayerAction; import mage.view.PlayerView; @@ -68,9 +71,10 @@ public class PlayAreaPanel extends javax.swing.JPanel { private boolean smallMode = false; private boolean playingMode = true; private final GamePanel gamePanel; - private final boolean playerItself; - - private JCheckBoxMenuItem manaPoolMenuItem; + private final PlayAreaPanelOptions options; + + private JCheckBoxMenuItem manaPoolMenuItem1; + private JCheckBoxMenuItem manaPoolMenuItem2; private JCheckBoxMenuItem allowViewHandCardsMenuItem; public static final int PANEL_HEIGHT = 242; @@ -80,25 +84,24 @@ public class PlayAreaPanel extends javax.swing.JPanel { * @param player * @param bigCard * @param gameId - * @param isPlayer true if the client is a player / false if the client is a watcher - * @param playerItself true if it's the area of the player itself * @param priorityTime - * @param gamePanel */ - public PlayAreaPanel(PlayerView player, BigCard bigCard, UUID gameId, boolean playerItself, int priorityTime, boolean isPlayer, GamePanel gamePanel) { - //this(isPlayer); - this.playerItself = playerItself; + * @param gamePanel + * @param options + */ + public PlayAreaPanel(PlayerView player, BigCard bigCard, UUID gameId, int priorityTime, GamePanel gamePanel, PlayAreaPanelOptions options) { + this.gamePanel = gamePanel; + this.options = options; initComponents(); setOpaque(false); battlefieldPanel.setOpaque(false); popupMenu = new JPopupMenu(); - if (isPlayer) { + if (options.isPlayer) { addPopupMenuPlayer(player.getUserData().allowRequestShowHandCards()); } else { addPopupMenuWatcher(); } this.add(popupMenu); - this.gamePanel = gamePanel; init(player, bigCard, gameId, priorityTime); update(player); } @@ -138,98 +141,135 @@ public class PlayAreaPanel extends javax.swing.JPanel { JMenuItem menuItem; - menuItem = new JMenuItem("F2 - Confirm"); - popupMenu.add(menuItem); - - // Confirm (F2) - menuItem.addActionListener(new ActionListener() { + ActionListener skipListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - if (gamePanel.getFeedbackPanel() != null) { - gamePanel.getFeedbackPanel().pressOKYesOrDone(); + switch (e.getActionCommand()) { + case "F2": { + if (gamePanel.getFeedbackPanel() != null) { + gamePanel.getFeedbackPanel().pressOKYesOrDone(); + } + break; + } + case "F3": { + gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_CANCEL_ALL_ACTIONS, gameId, null); + break; + } + case "F4": { + gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_NEXT_TURN, gameId, null); + break; + } + case "F5": { + gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_TURN_END_STEP, gameId, null); + break; + } + case "F7": { + gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_NEXT_MAIN_PHASE, gameId, null); + break; + } + case "F9": { + gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_MY_NEXT_TURN, gameId, null); + break; + } } } - }); - - - menuItem = new JMenuItem("F3 - Cancel previous F4/F9 skip action"); + }; + + menuItem = new JMenuItem("F2 - Confirm current request"); + menuItem.setActionCommand("F2"); + menuItem.setMnemonic(KeyEvent.VK_O); popupMenu.add(menuItem); + menuItem.addActionListener(skipListener); - // Cancel (F3) - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - gamePanel.getClient().sendPlayerAction(PlayerAction.PASS_PRIORITY_CANCEL_ALL_ACTIONS, gameId, null); - } - }); + menuItem = new JMenuItem("F3 - Cancel active skip action"); + menuItem.setActionCommand("F3"); + menuItem.setMnemonic(KeyEvent.VK_N); + popupMenu.add(menuItem); + menuItem.addActionListener(skipListener); + + + JMenu skipMenu = new JMenu("Skip"); + skipMenu.setMnemonic(KeyEvent.VK_S); + popupMenu.add(skipMenu); + + String tooltipText = "This skip actions stops if something goes to
stack and if attackers or blocker have to be declared."; + menuItem = new JMenuItem("F4 - Phases until next turn"); + menuItem.setActionCommand("F4"); + menuItem.setToolTipText(tooltipText); + menuItem.setMnemonic(KeyEvent.VK_T); + skipMenu.add(menuItem); + menuItem.addActionListener(skipListener); + + menuItem = new JMenuItem("F5 - Phases until next end step"); + menuItem.setActionCommand("F5"); + menuItem.setToolTipText(tooltipText); + menuItem.setMnemonic(KeyEvent.VK_E); + skipMenu.add(menuItem); + menuItem.addActionListener(skipListener); + + menuItem = new JMenuItem("F7 - Phases until begin of next main phase"); + menuItem.setToolTipText(tooltipText); + menuItem.setActionCommand("F7"); + menuItem.setMnemonic(KeyEvent.VK_M); + skipMenu.add(menuItem); + menuItem.addActionListener(skipListener); + + menuItem = new JMenuItem("F9 - Everything until your own next turn"); + menuItem.setActionCommand("F9"); + menuItem.setToolTipText(tooltipText); + menuItem.setMnemonic(KeyEvent.VK_N); + skipMenu.add(menuItem); + menuItem.addActionListener(skipListener); popupMenu.addSeparator(); - - menuItem = new JMenuItem("F4 - Skip phases until next turn (stop on stack/attack/block)"); - popupMenu.add(menuItem); - - // Skip to next turn (F4) - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - gamePanel.getClient().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_NEXT_TURN, gameId, null); - } - }); - - menuItem = new JMenuItem("F5 - Skip phases until next end step (stop on stack/attack/block)"); - popupMenu.add(menuItem); - - // Skip to next end step of turn (F5) - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - gamePanel.getClient().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_TURN_END_STEP, gameId, null); - } - }); - - menuItem = new JMenuItem("F7 - Skip phases until begin of next main phase (stop on stack/attack/block)"); - popupMenu.add(menuItem); - - // Skip to next main phase (F7) - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - gamePanel.getClient().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_NEXT_MAIN_PHASE, gameId, null); - } - }); - menuItem = new JMenuItem("F9 - Skip everything until own next turn (stop on attack/block)"); - popupMenu.add(menuItem); - - // Skip to next own turn (F9) - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - gamePanel.getClient().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_MY_NEXT_TURN, gameId, null); - } - }); - - popupMenu.addSeparator(); - - manaPoolMenuItem = new JCheckBoxMenuItem("Use mana from pool automatically", true); - manaPoolMenuItem.setMnemonic(KeyEvent.VK_M); - manaPoolMenuItem.setToolTipText("If not active, you have to click the type of mana you want to pay in the player panel."); - popupMenu.add(manaPoolMenuItem); + + JMenu manaPoolMenu = new JMenu("Mana payment"); + manaPoolMenu.setMnemonic(KeyEvent.VK_M); + popupMenu.add(manaPoolMenu); + + manaPoolMenuItem1 = new JCheckBoxMenuItem("Automatically", true); + manaPoolMenuItem1.setMnemonic(KeyEvent.VK_A); + manaPoolMenuItem1.setToolTipText("If not active, produced mana goes only to the mana pool
" + + "and you have to click the type of mana you want to use
" + + "in the player mana pool panel for payment."); + manaPoolMenu.add(manaPoolMenuItem1); // Auto pay mana from mana pool - manaPoolMenuItem.addActionListener(new ActionListener() { + manaPoolMenuItem1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { boolean manaPoolAutomatic = ((JCheckBoxMenuItem)e.getSource()).getState(); - gamePanel.setMenuStates(manaPoolAutomatic); - gamePanel.getClient().sendPlayerAction(manaPoolAutomatic ? PlayerAction.MANA_AUTO_PAYMENT_ON: PlayerAction.MANA_AUTO_PAYMENT_OFF, gameId, null); + PreferencesDialog.saveValue(KEY_GAME_MANA_AUTOPAYMENT, manaPoolAutomatic ? "true": "false"); + gamePanel.setMenuStates(manaPoolAutomatic, manaPoolMenuItem2.getState()); + gamePanel.getSession().sendPlayerAction(manaPoolAutomatic ? PlayerAction.MANA_AUTO_PAYMENT_ON: PlayerAction.MANA_AUTO_PAYMENT_OFF, gameId, null); + } + }); + manaPoolMenuItem2 = new JCheckBoxMenuItem("No automatic usage for mana already in the pool", true); + manaPoolMenuItem2.setMnemonic(KeyEvent.VK_N); + manaPoolMenuItem2.setToolTipText("Mana that is already in the mana pool as you start casting a spell or activating an ability
" + + " needs to be payed manually. So you use the mana in the pool only by clicking on the related
" + + " mana symbols of mana pool area."); + manaPoolMenu.add(manaPoolMenuItem2); + + // Auto pay mana from mana pool + manaPoolMenuItem2.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + boolean manaPoolAutomaticRestricted = ((JCheckBoxMenuItem)e.getSource()).getState(); + PreferencesDialog.saveValue(KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE, manaPoolAutomaticRestricted ? "true": "false"); + gamePanel.setMenuStates(manaPoolMenuItem1.getState(), manaPoolAutomaticRestricted); + gamePanel.getSession().sendPlayerAction(manaPoolAutomaticRestricted ? PlayerAction.MANA_AUTO_PAYMENT_RESTRICTED_ON: PlayerAction.MANA_AUTO_PAYMENT_RESTRICTED_OFF, gameId, null); } }); - + JMenu automaticConfirmsMenu = new JMenu("Automatic confirms"); + automaticConfirmsMenu.setMnemonic(KeyEvent.VK_U); + popupMenu.add(automaticConfirmsMenu); + menuItem = new JMenuItem("Replacement effects - reset auto select"); menuItem.setMnemonic(KeyEvent.VK_R); menuItem.setToolTipText("Reset all effects that were added to the list of auto select replacement effects this game."); - popupMenu.add(menuItem); + automaticConfirmsMenu.add(menuItem); // Reset the replacement effcts that were auto selected for the game menuItem.addActionListener(new ActionListener() { @Override @@ -238,11 +278,14 @@ public class PlayAreaPanel extends javax.swing.JPanel { } }); - popupMenu.addSeparator(); - - if (!playerItself) { - menuItem = new JMenuItem("Request permission to see hand cards"); - popupMenu.add(menuItem); + JMenu handCardsMenu = new JMenu("Cards on hand"); + handCardsMenu.setMnemonic(KeyEvent.VK_H); + popupMenu.add(handCardsMenu); + + if (!options.playerItself) { + menuItem = new JMenuItem("Request permission to see the hand cards"); + menuItem.setMnemonic(KeyEvent.VK_P); + handCardsMenu.add(menuItem); // Request to see hand cards menuItem.addActionListener(new ActionListener() { @@ -252,10 +295,10 @@ public class PlayAreaPanel extends javax.swing.JPanel { } }); } else { - allowViewHandCardsMenuItem = new JCheckBoxMenuItem("Allow requests to show your hand cards", allowRequestToShowHandCards); + allowViewHandCardsMenuItem = new JCheckBoxMenuItem("Allow requests to show from other users", allowRequestToShowHandCards); allowViewHandCardsMenuItem.setMnemonic(KeyEvent.VK_A); allowViewHandCardsMenuItem.setToolTipText("If activated watchers or other players can request to see your hand cards. If you grant this to a user, it's valid for the complete match."); - popupMenu.add(allowViewHandCardsMenuItem); + handCardsMenu.add(allowViewHandCardsMenuItem); // Requests allowed allowViewHandCardsMenuItem.addActionListener(new ActionListener() { @@ -268,9 +311,9 @@ public class PlayAreaPanel extends javax.swing.JPanel { }); menuItem = new JMenuItem("Revoke all permission(s) to see your hand cards"); - menuItem.setMnemonic(KeyEvent.VK_P); + menuItem.setMnemonic(KeyEvent.VK_R); menuItem.setToolTipText("Revoke already granted permission for all spectators to see your hand cards."); - popupMenu.add(menuItem); + handCardsMenu.add(menuItem); // revoke permissions to see hand cards menuItem.addActionListener(new ActionListener() { @@ -280,37 +323,90 @@ public class PlayAreaPanel extends javax.swing.JPanel { } }); } - popupMenu.addSeparator(); + + if (options.rollbackTurnsAllowed) { + ActionListener rollBackActionListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int turnsToRollBack = Integer.parseInt(e.getActionCommand()); + gamePanel.getSession().sendPlayerAction(PlayerAction.ROLLBACK_TURNS, gameId, turnsToRollBack); + } + }; + + JMenu rollbackMainItem = new JMenu("Roll back"); + rollbackMainItem.setMnemonic(KeyEvent.VK_R); + rollbackMainItem.setToolTipText("The game will be rolled back to the start of the requested turn if all players agree."); + popupMenu.add(rollbackMainItem); + + menuItem = new JMenuItem("To the start of the current turn"); + menuItem.setMnemonic(KeyEvent.VK_C); + menuItem.setActionCommand("0"); + menuItem.addActionListener(rollBackActionListener); + rollbackMainItem.add(menuItem); + + menuItem = new JMenuItem("To the start of the previous turn"); + menuItem.setMnemonic(KeyEvent.VK_P); + menuItem.setActionCommand("1"); + menuItem.addActionListener(rollBackActionListener); + rollbackMainItem.add(menuItem); - menuItem = new JMenuItem("Concede game"); - popupMenu.add(menuItem); + menuItem = new JMenuItem("The current turn and the 2 turns before"); + menuItem.setMnemonic(KeyEvent.VK_2); + menuItem.setActionCommand("2"); + menuItem.addActionListener(rollBackActionListener); + rollbackMainItem.add(menuItem); - // Concede - menuItem.addActionListener(new ActionListener() { + menuItem = new JMenuItem("The current turn and the 3 turns before"); + menuItem.setMnemonic(KeyEvent.VK_3); + menuItem.setActionCommand("3"); + menuItem.addActionListener(rollBackActionListener); + rollbackMainItem.add(menuItem); + + } + + + + JMenu concedeMenu = new JMenu("Concede"); + concedeMenu.setMnemonic(KeyEvent.VK_C); + popupMenu.add(concedeMenu); + + ActionListener concedeListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - if (JOptionPane.showConfirmDialog(PlayAreaPanel.this, "Are you sure you want to concede the game?", "Confirm concede game", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - MageFrame.getClient().sendPlayerAction(PlayerAction.CONCEDE, gameId, null); + switch (e.getActionCommand()) { + case "Game": { + if (JOptionPane.showConfirmDialog(PlayAreaPanel.this, "Are you sure you want to concede the game?", "Confirm concede game", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + MageFrame.getSession().sendPlayerAction(PlayerAction.CONCEDE, gameId, null); + } + break; + } + case "Match": { + if (JOptionPane.showConfirmDialog(PlayAreaPanel.this, "Are you sure you want to concede the complete match?", "Confirm concede match", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + MageFrame.getSession().quitMatch(gameId); + } + break; + } } } - }); - - popupMenu.addSeparator(); - - menuItem = new JMenuItem("Concede complete match"); - popupMenu.add(menuItem); - - // Quit match - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (JOptionPane.showConfirmDialog(PlayAreaPanel.this, "Are you sure you want to concede the complete match?", "Confirm concede match", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - MageFrame.getClient().quitMatch(gameId); - } - } - }); - + }; + + // Concede Game + menuItem = new JMenuItem("Game"); + menuItem.setMnemonic(KeyEvent.VK_G); + menuItem.setActionCommand("Game"); + menuItem.setToolTipText("Concedes only the current game and after that the next game of the match is started if there is another game needed."); + concedeMenu.add(menuItem); + menuItem.addActionListener(concedeListener); + + // Concede Match + menuItem = new JMenuItem("Match"); + menuItem.setMnemonic(KeyEvent.VK_M); + menuItem.setActionCommand("Match"); + menuItem.setToolTipText("Concedes the complete match. So if you're in a tournament you finish the current tournament round."); + concedeMenu.add(menuItem); + menuItem.addActionListener(concedeListener); + battlefieldPanel.getMainPanel().addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent Me) { @@ -472,8 +568,9 @@ public class PlayAreaPanel extends javax.swing.JPanel { this.playingMode = playingMode; } - public void setMenuStates(boolean manaPoolAutomatic) { - manaPoolMenuItem.setSelected(manaPoolAutomatic); + public void setMenuStates(boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted) { + manaPoolMenuItem1.setSelected(manaPoolAutomatic); + manaPoolMenuItem2.setSelected(manaPoolAutomaticRestricted); } private mage.client.game.BattlefieldPanel battlefieldPanel; diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java new file mode 100644 index 00000000000..377b7591c4b --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java @@ -0,0 +1,58 @@ +/* +* 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.client.game; + +/** + * Defines some options for the PlayAreaPanel + * + * @author LevelX2 + */ +public class PlayAreaPanelOptions { + + public PlayAreaPanelOptions(boolean isPlayer, boolean playerItself, boolean rollbackTurnsAllowed) { + this.isPlayer = isPlayer; + this.playerItself = playerItself; + this.rollbackTurnsAllowed = rollbackTurnsAllowed; + } + + /** + * true if the client is a player / false if the client is a watcher + */ + public boolean isPlayer = false; + + /** + * true if the player is the client player itself, false if the player is another player playing with the clinet player + */ + public boolean playerItself = false; + + /** + * true if the player can roll back turns if all players agree + */ + public boolean rollbackTurnsAllowed = false; + +} diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index 7eb8cea2213..b21749765d0 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -64,12 +64,10 @@ import javax.swing.JInternalFrame; import javax.swing.JLayeredPane; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; -import javax.swing.JTable; import javax.swing.JToggleButton; import javax.swing.RowFilter; import javax.swing.SwingWorker; import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableColumn; import mage.cards.decks.importer.DeckImporterUtil; import mage.client.MageFrame; import mage.client.chat.ChatPanel; @@ -78,10 +76,13 @@ import mage.client.dialog.JoinTableDialog; import mage.client.dialog.NewTableDialog; import mage.client.dialog.NewTournamentDialog; import mage.client.dialog.PreferencesDialog; +import static mage.client.dialog.PreferencesDialog.KEY_TABLES_COLUMNS_ORDER; +import static mage.client.dialog.PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH; import mage.client.dialog.TableWaitingDialog; import mage.client.util.ButtonColumn; import mage.client.util.MageTableRowSorter; import mage.client.util.gui.GuiDisplayUtil; +import mage.client.util.gui.TableUtil; import mage.constants.MatchTimeLimit; import mage.constants.MultiplayerAttackOption; import mage.constants.RangeOfInfluence; @@ -94,7 +95,6 @@ import mage.view.RoomUsersView; import mage.view.RoomView; import mage.view.TableView; import org.apache.log4j.Logger; -import org.mage.card.arcane.Util; import org.mage.network.Client; /** @@ -122,9 +122,11 @@ public class TablesPanel extends javax.swing.JPanel { JToggleButton[] filterButtons; + private static final int[] defaultColumnsWidth = {35, 150, 120, 180, 80, 120, 80, 60, 60}; + /** Creates new form TablesPanel */ public TablesPanel() { - + tableModel = new TableTableModel(); matchesModel = new MatchesTableModel(); gameChooser = new GameChooser(); @@ -137,7 +139,8 @@ public class TablesPanel extends javax.swing.JPanel { activeTablesSorter = new MageTableRowSorter(tableModel); tableTables.setRowSorter(activeTablesSorter); - TableTableModel.setColumnWidthAndOrder(tableTables); + TableUtil.setColumnWidthAndOrder(tableTables, defaultColumnsWidth, + PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH, PreferencesDialog.KEY_TABLES_COLUMNS_ORDER); tableCompleted.setRowSorter(new MageTableRowSorter(matchesModel)); @@ -148,7 +151,8 @@ public class TablesPanel extends javax.swing.JPanel { filterButtons = new JToggleButton[] {btnStateWaiting, btnStateActive, btnStateFinished, btnTypeMatch, btnTypeTourneyConstructed, btnTypeTourneyLimited, - btnFormatBlock, btnFormatStandard, btnFormatModern, btnFormatLegacy, btnFormatVintage, btnFormatCommander, btnFormatTinyLeader, btnFormatLimited, btnFormatOther}; + btnFormatBlock, btnFormatStandard, btnFormatModern, btnFormatLegacy, btnFormatVintage, btnFormatCommander, btnFormatTinyLeader, btnFormatLimited, btnFormatOther, + btnSkillBeginner, btnSkillCasual, btnSkillSerious }; JComponent[] components = new JComponent[] {chatPanel, jSplitPane1, jScrollPane1, jScrollPane2, topPanel, jPanel3}; for (JComponent component : components) { @@ -277,6 +281,7 @@ public class TablesPanel extends javax.swing.JPanel { public void cleanUp() { saveSettings(); + chatPanel.cleanUp(); } private void saveDividerLocations() { @@ -312,23 +317,7 @@ public class TablesPanel extends javax.swing.JPanel { } PreferencesDialog.saveValue(PreferencesDialog.KEY_TABLES_FILTER_SETTINGS, formatSettings.toString()); - // Column width - StringBuilder columnWidthSettings = new StringBuilder(); - StringBuilder columnOrderSettings = new StringBuilder(); - boolean firstValue = true; - for (int i = 0; i < tableTables.getColumnModel().getColumnCount(); i++) { - TableColumn column = tableTables.getColumnModel().getColumn(tableTables.convertColumnIndexToView(i)); - if (!firstValue) { - columnWidthSettings.append(","); - columnOrderSettings.append(","); - } else { - firstValue = false; - } - columnWidthSettings.append(column.getWidth()); - columnOrderSettings.append(tableTables.convertColumnIndexToModel(i)); - } - PreferencesDialog.saveValue(PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH, columnWidthSettings.toString()); - PreferencesDialog.saveValue(PreferencesDialog.KEY_TABLES_COLUMNS_ORDER, columnOrderSettings.toString()); + TableUtil.saveColumnWidthAndOrderToPrefs(tableTables, KEY_TABLES_COLUMNS_WIDTH, KEY_TABLES_COLUMNS_ORDER); } private void restoreDividerLocations() { @@ -1158,6 +1147,7 @@ public class TablesPanel extends javax.swing.JPanel { options.setWinsNeeded(1); options.setMatchTimeLimit(MatchTimeLimit.NONE); options.setFreeMulligans(2); + options.setSkillLevel(SkillLevel.CASUAL); table = client.createTable(roomId, options); client.joinTable(roomId, table.getTableId(), "Human", "Human", 1, DeckImporterUtil.importDeck("test.dck"),""); @@ -1263,8 +1253,7 @@ class TableTableModel extends AbstractTableModel { public static final int ACTION_COLUMN = 8; // column the action is located (starting with 0) private final String[] columnNames = new String[]{"M/T","Deck Type", "Owner / Players", "Game Type", "Info", "Status", "Created / Started", "Skill Level", "Action"}; - private static final int[] defaultColumnsWidth = {35, 150, 120, 180, 80, 120, 80, 60, 60}; - + private TableView[] tables = new TableView[0]; private static final DateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss");; @@ -1273,32 +1262,7 @@ class TableTableModel extends AbstractTableModel { public void loadData(Collection tables) throws MageRemoteException { this.tables = tables.toArray(new TableView[0]); this.fireTableDataChanged(); - } - - static public void setColumnWidthAndOrder(JTable table) { - table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - - // set the column width from saved value or defaults - int[] widths = Util.getIntArrayFromString(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH, null)); - int i = 0; - for (int width : defaultColumnsWidth) { - if (widths != null && widths.length > i) { - width = widths[i]; - } - TableColumn column = table.getColumnModel().getColumn(i++); - column.setWidth(width); - column.setPreferredWidth(width); - } - - // set the column order - int[] order = Util.getIntArrayFromString(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_TABLES_COLUMNS_ORDER, null)); - if (order != null && order.length == table.getColumnCount()) { - for (int j = 0; j < table.getColumnCount(); j++) { - table.moveColumn(table.convertColumnIndexToView(order[j]), j); - } - } - - } + } @Override public int getRowCount() { diff --git a/Mage.Client/src/main/java/mage/client/util/gui/ColorsChooser.java b/Mage.Client/src/main/java/mage/client/util/gui/ColorsChooser.java index bea63c29f89..a2eae35b36d 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/ColorsChooser.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/ColorsChooser.java @@ -78,7 +78,7 @@ public class ColorsChooser extends JComboBox implements ListCellRenderer { } private void drawOn(JPanel panel, String value) { - List images = new ArrayList(); + List images = new ArrayList<>(); value = value.toUpperCase(); for (int i = 0; i < value.length(); i++) { char symbol = value.charAt(i); diff --git a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java index 0c178372735..8252189c8c0 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java @@ -13,7 +13,6 @@ import org.mage.card.arcane.UI; import javax.swing.*; import java.awt.*; import java.util.ArrayList; -import mage.client.dialog.MageDialog; import mage.constants.Rarity; public class GuiDisplayUtil { diff --git a/Mage.Client/src/main/java/mage/client/util/gui/TableUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/TableUtil.java new file mode 100644 index 00000000000..187453aa5e1 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/TableUtil.java @@ -0,0 +1,87 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.client.util.gui; + +import javax.swing.JTable; +import javax.swing.table.TableColumn; +import mage.client.dialog.PreferencesDialog; +import org.mage.card.arcane.Util; + +/** + * + * @author LevelX2 + */ +public class TableUtil { + + /** + * + * @param table + * @param defaultColumnsWidth + * @param widthPrefKey + * @param orderPrefKey + */ + static public void setColumnWidthAndOrder(JTable table, int[] defaultColumnsWidth, String widthPrefKey, String orderPrefKey) { + table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + + // set the column width from saved value or defaults + int[] widths = getIntArrayFromString(PreferencesDialog.getCachedValue(widthPrefKey, null)); + int i = 0; + for (int width : defaultColumnsWidth) { + if (widths != null && widths.length > i) { + width = widths[i]; + } + TableColumn column = table.getColumnModel().getColumn(i++); + column.setWidth(width); + column.setPreferredWidth(width); + } + + // set the column order + int[] order = getIntArrayFromString(PreferencesDialog.getCachedValue(orderPrefKey, null)); + if (order != null && order.length == table.getColumnCount()) { + for (int j = 0; j < table.getColumnCount(); j++) { + table.moveColumn(table.convertColumnIndexToView(order[j]), j); + } + } + + } + + static public void saveColumnWidthAndOrderToPrefs(JTable table, String widthPrefKey, String orderPrefKey) { + // Column width + StringBuilder columnWidthSettings = new StringBuilder(); + StringBuilder columnOrderSettings = new StringBuilder(); + boolean firstValue = true; + for (int i = 0; i < table.getColumnModel().getColumnCount(); i++) { + TableColumn column = table.getColumnModel().getColumn(table.convertColumnIndexToView(i)); + if (!firstValue) { + columnWidthSettings.append(","); + columnOrderSettings.append(","); + } else { + firstValue = false; + } + columnWidthSettings.append(column.getWidth()); + columnOrderSettings.append(table.convertColumnIndexToModel(i)); + } + PreferencesDialog.saveValue(widthPrefKey, columnWidthSettings.toString()); + PreferencesDialog.saveValue(orderPrefKey, columnOrderSettings.toString()); + + } + + + public static int[] getIntArrayFromString(String stringData) { + int[] intArray = null; + if (stringData != null && !stringData.isEmpty()) { + String[] items = stringData.split(","); + int lengthW = items.length; + intArray = new int[lengthW]; + for (int i = 0; i < lengthW; i++) { + try { + intArray[i] = Integer.parseInt(items[i]); + } catch (NumberFormatException nfe) {} + } + } + return intArray; + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryComboBox.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryComboBox.java new file mode 100644 index 00000000000..d9687d13261 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryComboBox.java @@ -0,0 +1,320 @@ +/* + * 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.client.util.gui.countryBox; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; + +/** + * A custom combo box with its own renderer and editor. + * @author wwww.codejava.net + * + */ +public class CountryComboBox extends JComboBox { + + private final DefaultComboBoxModel model; + + public static String[][] countryList = { + {"Afghanistan", "af"}, + {"Åland Islands", "ax"}, + {"Albania", "al"}, + {"Algeria", "dz"}, + {"American Samoa", "as"}, + {"Andorra", "ad"}, + {"Angola", "an"}, + {"Anguilla", "ai"}, + {"Antarctica", "ao"}, + {"Antigua and Barbuda", "ag"}, + {"Argentina", "ar"}, + {"Armenia", "am"}, + {"Aruba", "aw"}, + {"Australia", "au"}, + {"Austria", "at"}, + {"Azerbaijan", "az"}, + {"Bahamas", "bs"}, + {"Bahrain", "bh"}, + {"Bangladesh", "bd"}, + {"Barbados", "bb"}, + {"Belarus", "by"}, + {"Belgium", "be"}, + {"Belize", "bz"}, + {"Benin", "bj"}, + {"Bermuda", "bm"}, + {"Bhutan", "bt"}, + {"Bolivia, Plurinational State of", "bo"}, + {"Bosnia and Herzegovina", "ba"}, + {"Botswana", "bw"}, + {"Bouvet Island", "bv"}, + {"Brazil", "br"}, + {"British Indian Ocean Territory", "io"}, + {"Brunei Darussalam", "bn"}, + {"Bulgaria", "bg"}, + {"Burkina Faso", "bf"}, + {"Burundi", "bi"}, + {"Cabo Verde", "cv"}, + {"Cambodia", "kh"}, + {"Cameroon", "cm"}, + {"Canada", "ca"}, + {"Catalonia", "catalonia"}, + {"Cayman Islands", "ky"}, + {"Central African Republic", "cf"}, + {"Chad", "td"}, + {"Chile", "cl"}, + {"China", "cn"}, + {"Christmas Island", "cx"}, + {"Cocos (Keeling) Islands", "cc"}, + {"Colombia", "co"}, + {"Comoros", "km"}, + {"Congo", "cg"}, + {"Congo, the Democratic Republic of the", "cd"}, + {"Cook Islands", "ck"}, + {"Costa Rica", "cr"}, + {"Côte d'Ivoire", "ci"}, + {"Croatia", "hr"}, + {"Cuba", "cu"}, + {"Cyprus", "cy"}, + {"Czech Republic", "cz"}, + {"Denmark", "dk"}, + {"Djibouti", "dj"}, + {"Dominica", "dm"}, + {"Dominican Republic", "do"}, + {"Ecuador", "ec"}, + {"Egypt", "eg"}, + {"El Salvador", "sv"}, + {"England", "england"}, + {"Equatorial Guinea", "gq"}, + {"Eritrea", "er"}, + {"Estonia", "ee"}, + {"Ethiopia", "et"}, + {"European Union", "europeanunion"}, + {"Falkland Islands (Malvinas)", "fk"}, + {"Faroe Islands", "fo"}, + {"Fiji", "fj"}, + {"Finland", "fi"}, + {"France", "fr"}, + {"French Guiana", "gf"}, + {"French Polynesia", "pf"}, + {"French Southern Territories", "tf"}, + {"Gabon", "ga"}, + {"Gambia", "gm"}, + {"Georgia", "ge"}, + {"Germany", "de"}, + {"Ghana", "gh"}, + {"Gibraltar", "gi"}, + {"Greece", "gr"}, + {"Greenland", "gl"}, + {"Grenada", "gd"}, + {"Guadeloupe", "gp"}, + {"Guam", "gu"}, + {"Guatemala", "gt"}, + {"Guinea", "gn"}, + {"Guinea-Bissau", "gw"}, + {"Guyana", "gy"}, + {"Haiti", "ht"}, + {"Heard Island and McDonald Islands", "hm"}, + {"Holy See", "va"}, + {"Honduras", "hn"}, + {"Hong Kong", "hk"}, + {"Hungary", "hu"}, + {"Iceland", "is"}, + {"India", "in"}, + {"Indonesia", "id"}, + {"Iran, Islamic Republic of", "ir"}, + {"Iraq", "iq"}, + {"Ireland", "ie"}, + {"Israel", "il"}, + {"Italy", "it"}, + {"Jamaica", "jm"}, + {"Japan", "jp"}, + {"Jordan", "jo"}, + {"Kazakhstan", "kz"}, + {"Kenya", "ke"}, + {"Kiribati", "ki"}, + {"Korea, Democratic People's Republic of", "kp"}, + {"Korea, Republic of", "kr"}, + {"Kuwait", "kw"}, + {"Kyrgyzstan", "kg"}, + {"Lao People's Democratic Republic", "la"}, + {"Latvia", "lv"}, + {"Lebanon", "lb"}, + {"Lesotho", "ls"}, + {"Liberia", "lr"}, + {"Libya", "ly"}, + {"Liechtenstein", "li"}, + {"Lithuania", "lt"}, + {"Luxembourg", "lu"}, + {"Macao", "mo"}, + {"Macedonia, the former Yugoslav Republic of", "mk"}, + {"Madagascar", "mg"}, + {"Malawi", "mw"}, + {"Malaysia", "my"}, + {"Maldives", "mv"}, + {"Mali", "ml"}, + {"Malta", "mt"}, + {"Marshall Islands", "mh"}, + {"Martinique", "mq"}, + {"Mauritania", "mr"}, + {"Mauritius", "mu"}, + {"Mayotte", "yt"}, + {"Mexico", "mx"}, + {"Micronesia, Federated States of", "fm"}, + {"Moldova, Republic of", "md"}, + {"Monaco", "mc"}, + {"Mongolia", "mn"}, + {"Montenegro", "me"}, + {"Montserrat", "ms"}, + {"Morocco", "ma"}, + {"Mozambique", "mz"}, + {"Myanmar", "mm"}, + {"Namibia", "na"}, + {"Nauru", "nr"}, + {"Nepal", "np"}, + {"Netherlands", "nl"}, + {"New Caledonia", "nc"}, + {"New Zealand", "nz"}, + {"Nicaragua", "ni"}, + {"Niger", "ne"}, + {"Nigeria", "ng"}, + {"Niue", "nu"}, + {"Norfolk Island", "nf"}, + {"Northern Mariana Islands", "mp"}, + {"Norway", "no"}, + {"Oman", "om"}, + {"Pakistan", "pk"}, + {"Palau", "pw"}, + {"Palestine, State of", "ps"}, + {"Panama", "pa"}, + {"Papua New Guinea", "pg"}, + {"Paraguay", "py"}, + {"Peru", "pe"}, + {"Philippines", "ph"}, + {"Pitcairn", "pn"}, + {"Poland", "pl"}, + {"Portugal", "pt"}, + {"Puerto Rico", "pr"}, + {"Qatar", "qa"}, + {"Réunion", "re"}, + {"Romania", "ro"}, + {"Russian Federation", "ru"}, + {"Rwanda", "rw"}, + {"Saint Helena, Ascension and Tristan da Cunha", "sh"}, + {"Saint Kitts and Nevis", "kn"}, + {"Saint Lucia", "lc"}, + {"Saint Pierre and Miquelon", "pm"}, + {"Saint Vincent and the Grenadines", "vc"}, + {"Samoa", "ws"}, + {"San Marino", "sm"}, + {"Sao Tome and Principe", "st"}, + {"Saudi Arabia", "sa"}, + {"Scotland", "scotland"}, + {"Senegal", "sn"}, + {"Serbia", "rs"}, + {"Seychelles", "sc"}, + {"Sierra Leone", "sl"}, + {"Singapore", "sg"}, + {"Slovakia", "sk"}, + {"Slovenia", "si"}, + {"Solomon Islands", "sb"}, + {"Somalia", "so"}, + {"South Africa", "za"}, + {"South Georgia and the South Sandwich Islands", "gs"}, + {"Spain", "es"}, + {"Sri Lanka", "lk"}, + {"Sudan", "sd"}, + {"Suriname", "sr"}, + {"Svalbard and Jan Mayen", "sj"}, + {"Swaziland", "sz"}, + {"Sweden", "se"}, + {"Switzerland", "ch"}, + {"Syrian Arab Republic", "sy"}, + {"Taiwan, Province of China", "tw"}, + {"Tajikistan", "tj"}, + {"Tanzania, United Republic of", "tz"}, + {"Thailand", "th"}, + {"Timor-Leste", "tl"}, + {"Togo", "tg"}, + {"Tokelau", "tk"}, + {"Tonga", "to"}, + {"Trinidad and Tobago", "tt"}, + {"Tunisia", "tn"}, + {"Turkey", "tr"}, + {"Turkmenistan", "tm"}, + {"Turks and Caicos Islands", "tc"}, + {"Tuvalu", "tv"}, + {"Uganda", "ug"}, + {"Ukraine", "ua"}, + {"United Arab Emirates", "ae"}, + {"United Kingdom of Great Britain and Northern Ireland", "gb"}, + {"United States Minor Outlying Islands", "um"}, + {"United States of America", "us"}, + {"Uruguay", "uy"}, + {"Uzbekistan", "uz"}, + {"Vanuatu", "vu"}, + {"Venezuela, Bolivarian Republic of", "ve"}, + {"Viet Nam", "vn"}, + {"Virgin Islands, British", "vg"}, + {"Virgin Islands, U.S.", "vi"}, + {"Wales", "wales"}, + {"Wallis and Futuna", "wf"}, + {"World", "world"}, + {"Western Sahara", "eh"}, + {"Yemen", "ye"}, + {"Zambia", "zm"}, + {"Zimbabwe", "zw"}, + }; + + @SuppressWarnings("unchecked") + public CountryComboBox() { + model = new DefaultComboBoxModel(); + setModel(model); + setRenderer(new CountryItemRenderer()); + setEditor(new CountryItemEditor()); + addItems(countryList); + } + + /** + * Add an array items to this combo box. + * Each item is an array of two String elements: + * - first element is country name. + * - second element is path of an image file for country flag. + * @param items + */ + @SuppressWarnings("unchecked") + public void addItems(String[][] items) { + for (String[] anItem : items) { + model.addElement(anItem); + } + } + + @Override + public Object getSelectedItem() { + return super.getSelectedItem(); //To change body of generated methods, choose Tools | Templates. + } + + +} diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemEditor.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemEditor.java new file mode 100644 index 00000000000..3bd0ee153d8 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemEditor.java @@ -0,0 +1,95 @@ +/* + * 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.client.util.gui.countryBox; + +import java.awt.Color; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.LineBorder; +import javax.swing.plaf.basic.BasicComboBoxEditor; + + +/** + * Editor for JComboBox + * @author wwww.codejava.net + * + */ +public class CountryItemEditor extends BasicComboBoxEditor { + private final JPanel panel = new JPanel(); + private final JLabel labelItem = new JLabel(); + private String selectedValue; + private String selectedImage; + + public CountryItemEditor() { + panel.setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.weightx = 1.0; +// constraints.insets = new Insets(2, 5, 2, 2); + constraints.insets = new Insets(0, 5, 0, 0); + + labelItem.setOpaque(false); + labelItem.setHorizontalAlignment(JLabel.LEFT); + labelItem.setForeground(Color.WHITE); + + panel.add(labelItem, constraints); +// panel.setBackground(Color.WHITE); + panel.setBackground(new Color(0, 100,190, 255)); + selectedValue = null; + } + + @Override + public Component getEditorComponent() { + return this.panel; + } + + @Override + public Object getItem() { + return this.selectedValue; + } + + public String getImageItem() { + return this.selectedImage; + } + @Override + public void setItem(Object item) { + if (item == null || !(item instanceof String[])) { + return; + } + String[] countryItem = (String[]) item; + selectedValue = countryItem[0]; + selectedImage = countryItem[1]; + labelItem.setText(selectedValue); + labelItem.setIcon(new ImageIcon(getClass().getResource("/flags/"+ countryItem[1] + ".png"))); + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemRenderer.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemRenderer.java new file mode 100644 index 00000000000..38bfaa4867c --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemRenderer.java @@ -0,0 +1,87 @@ +/* + * 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.client.util.gui.countryBox; + +import java.awt.Color; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; + + +/** + * Customer renderer for JComboBox + * @author www.codejava.net + * + */ + +public class CountryItemRenderer extends JPanel implements ListCellRenderer { + private final JLabel labelItem = new JLabel(); + + public CountryItemRenderer() { + setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.weightx = 1.0; + constraints.insets = new Insets(0, 0, 0, 0); + + labelItem.setOpaque(true); + labelItem.setHorizontalAlignment(JLabel.LEFT); + + add(labelItem, constraints); + setBackground(Color.LIGHT_GRAY); + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + String[] countryItem = (String[]) value; + + // set country name + labelItem.setText(countryItem[0]); + + // set country flag + labelItem.setIcon(new ImageIcon(countryItem[1])); + + if (isSelected) { + labelItem.setBackground(Color.BLUE); + labelItem.setForeground(Color.YELLOW); + } else { + labelItem.setForeground(Color.BLACK); + labelItem.setBackground(Color.LIGHT_GRAY); + } + + return this; + } + +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxEditor.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxEditor.java new file mode 100644 index 00000000000..6c8db0b4723 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxEditor.java @@ -0,0 +1,77 @@ +/* + * 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.client.util.gui.countryBox; + +import java.awt.Color; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Font; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.plaf.basic.BasicComboBoxEditor; + +/** + * + * @author LevelX2 + */ + +public class MyComboBoxEditor extends BasicComboBoxEditor { + private final JLabel label; + private final JPanel panel; + private Object selectedItem; + + public MyComboBoxEditor() { + label = new JLabel(); + panel = new JPanel(); + + label.setOpaque(false); + label.setFont(new Font("Arial", Font.BOLD, 14)); + label.setForeground(Color.BLACK); + + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 2)); + panel.add(label); + panel.setBackground(Color.BLUE); + } + + @Override + public Component getEditorComponent() { + return this.panel; + } + + @Override + public Object getItem() { + return "[" + this.selectedItem.toString() + "]"; + } + + @Override + public void setItem(Object item) { + this.selectedItem = item; + label.setText(item.toString()); + } + +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxRenderer.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxRenderer.java new file mode 100644 index 00000000000..d4c755dfca7 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxRenderer.java @@ -0,0 +1,57 @@ +/* + * 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.client.util.gui.countryBox; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +/** + * + * @author LevelX2 + */ +public class MyComboBoxRenderer extends JLabel implements ListCellRenderer { + + public MyComboBoxRenderer() { + setOpaque(true); + setFont(new Font("Arial", Font.BOLD /*| Font.ITALIC*/, 14)); + setBackground(Color.BLUE); + setForeground(Color.YELLOW); + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + setText(value.toString()); + return this; + } + +} diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/Util.java b/Mage.Client/src/main/java/org/mage/card/arcane/Util.java index 6e608945d3d..28b26feed93 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/Util.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/Util.java @@ -89,19 +89,5 @@ public class Util { throw new RuntimeException("Error invoking runnable in UI thread.", ex); } } - - public static int[] getIntArrayFromString(String stringData) { - int[] intArray = null; - if (stringData != null && !stringData.isEmpty()) { - String[] items = stringData.split(","); - int lengthW = items.length; - intArray = new int[lengthW]; - for (int i = 0; i < lengthW; i++) { - try { - intArray[i] = Integer.parseInt(items[i]); - } catch (NumberFormatException nfe) {} - } - } - return intArray; - } + } diff --git a/Mage.Client/src/main/resources/flags/ad.png b/Mage.Client/src/main/resources/flags/ad.png new file mode 100644 index 00000000000..625ca84f9ec Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ad.png differ diff --git a/Mage.Client/src/main/resources/flags/ae.png b/Mage.Client/src/main/resources/flags/ae.png new file mode 100644 index 00000000000..ef3a1ecfccd Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ae.png differ diff --git a/Mage.Client/src/main/resources/flags/af.png b/Mage.Client/src/main/resources/flags/af.png new file mode 100644 index 00000000000..a4742e299f5 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/af.png differ diff --git a/Mage.Client/src/main/resources/flags/ag.png b/Mage.Client/src/main/resources/flags/ag.png new file mode 100644 index 00000000000..556d5504dc2 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ag.png differ diff --git a/Mage.Client/src/main/resources/flags/ai.png b/Mage.Client/src/main/resources/flags/ai.png new file mode 100644 index 00000000000..74ed29d9261 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ai.png differ diff --git a/Mage.Client/src/main/resources/flags/al.png b/Mage.Client/src/main/resources/flags/al.png new file mode 100644 index 00000000000..92354cb6e25 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/al.png differ diff --git a/Mage.Client/src/main/resources/flags/am.png b/Mage.Client/src/main/resources/flags/am.png new file mode 100644 index 00000000000..344a2a86c43 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/am.png differ diff --git a/Mage.Client/src/main/resources/flags/an.png b/Mage.Client/src/main/resources/flags/an.png new file mode 100644 index 00000000000..633e4b89fde Binary files /dev/null and b/Mage.Client/src/main/resources/flags/an.png differ diff --git a/Mage.Client/src/main/resources/flags/ao.png b/Mage.Client/src/main/resources/flags/ao.png new file mode 100644 index 00000000000..bcbd1d6d40d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ao.png differ diff --git a/Mage.Client/src/main/resources/flags/ar.png b/Mage.Client/src/main/resources/flags/ar.png new file mode 100644 index 00000000000..e5ef8f1fcdd Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ar.png differ diff --git a/Mage.Client/src/main/resources/flags/as.png b/Mage.Client/src/main/resources/flags/as.png new file mode 100644 index 00000000000..32f30e4ce4e Binary files /dev/null and b/Mage.Client/src/main/resources/flags/as.png differ diff --git a/Mage.Client/src/main/resources/flags/at.png b/Mage.Client/src/main/resources/flags/at.png new file mode 100644 index 00000000000..0f15f34f288 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/at.png differ diff --git a/Mage.Client/src/main/resources/flags/au.png b/Mage.Client/src/main/resources/flags/au.png new file mode 100644 index 00000000000..a01389a745d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/au.png differ diff --git a/Mage.Client/src/main/resources/flags/aw.png b/Mage.Client/src/main/resources/flags/aw.png new file mode 100644 index 00000000000..a3579c2d621 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/aw.png differ diff --git a/Mage.Client/src/main/resources/flags/ax.png b/Mage.Client/src/main/resources/flags/ax.png new file mode 100644 index 00000000000..1eea80a7b73 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ax.png differ diff --git a/Mage.Client/src/main/resources/flags/az.png b/Mage.Client/src/main/resources/flags/az.png new file mode 100644 index 00000000000..4ee9fe5ced2 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/az.png differ diff --git a/Mage.Client/src/main/resources/flags/ba.png b/Mage.Client/src/main/resources/flags/ba.png new file mode 100644 index 00000000000..c77499249c9 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ba.png differ diff --git a/Mage.Client/src/main/resources/flags/bb.png b/Mage.Client/src/main/resources/flags/bb.png new file mode 100644 index 00000000000..0df19c71d20 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bb.png differ diff --git a/Mage.Client/src/main/resources/flags/bd.png b/Mage.Client/src/main/resources/flags/bd.png new file mode 100644 index 00000000000..076a8bf87c0 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bd.png differ diff --git a/Mage.Client/src/main/resources/flags/be.png b/Mage.Client/src/main/resources/flags/be.png new file mode 100644 index 00000000000..d86ebc800a6 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/be.png differ diff --git a/Mage.Client/src/main/resources/flags/bf.png b/Mage.Client/src/main/resources/flags/bf.png new file mode 100644 index 00000000000..ab5ce8fe123 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bf.png differ diff --git a/Mage.Client/src/main/resources/flags/bg.png b/Mage.Client/src/main/resources/flags/bg.png new file mode 100644 index 00000000000..0469f0607dc Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bg.png differ diff --git a/Mage.Client/src/main/resources/flags/bh.png b/Mage.Client/src/main/resources/flags/bh.png new file mode 100644 index 00000000000..ea8ce68761b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bh.png differ diff --git a/Mage.Client/src/main/resources/flags/bi.png b/Mage.Client/src/main/resources/flags/bi.png new file mode 100644 index 00000000000..5cc2e30cfc4 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bi.png differ diff --git a/Mage.Client/src/main/resources/flags/bj.png b/Mage.Client/src/main/resources/flags/bj.png new file mode 100644 index 00000000000..1cc8b458a4c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bj.png differ diff --git a/Mage.Client/src/main/resources/flags/bm.png b/Mage.Client/src/main/resources/flags/bm.png new file mode 100644 index 00000000000..c0c7aead8df Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bm.png differ diff --git a/Mage.Client/src/main/resources/flags/bn.png b/Mage.Client/src/main/resources/flags/bn.png new file mode 100644 index 00000000000..8fb09849e9b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bn.png differ diff --git a/Mage.Client/src/main/resources/flags/bo.png b/Mage.Client/src/main/resources/flags/bo.png new file mode 100644 index 00000000000..ce7ba522aa7 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bo.png differ diff --git a/Mage.Client/src/main/resources/flags/br.png b/Mage.Client/src/main/resources/flags/br.png new file mode 100644 index 00000000000..9b1a5538b26 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/br.png differ diff --git a/Mage.Client/src/main/resources/flags/bs.png b/Mage.Client/src/main/resources/flags/bs.png new file mode 100644 index 00000000000..639fa6cfa9c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bs.png differ diff --git a/Mage.Client/src/main/resources/flags/bt.png b/Mage.Client/src/main/resources/flags/bt.png new file mode 100644 index 00000000000..1d512dfff42 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bt.png differ diff --git a/Mage.Client/src/main/resources/flags/bv.png b/Mage.Client/src/main/resources/flags/bv.png new file mode 100644 index 00000000000..160b6b5b79d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bv.png differ diff --git a/Mage.Client/src/main/resources/flags/bw.png b/Mage.Client/src/main/resources/flags/bw.png new file mode 100644 index 00000000000..fcb10394152 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bw.png differ diff --git a/Mage.Client/src/main/resources/flags/by.png b/Mage.Client/src/main/resources/flags/by.png new file mode 100644 index 00000000000..504774ec10e Binary files /dev/null and b/Mage.Client/src/main/resources/flags/by.png differ diff --git a/Mage.Client/src/main/resources/flags/bz.png b/Mage.Client/src/main/resources/flags/bz.png new file mode 100644 index 00000000000..be63ee1c623 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/bz.png differ diff --git a/Mage.Client/src/main/resources/flags/ca.png b/Mage.Client/src/main/resources/flags/ca.png new file mode 100644 index 00000000000..1f204193ae5 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ca.png differ diff --git a/Mage.Client/src/main/resources/flags/catalonia.png b/Mage.Client/src/main/resources/flags/catalonia.png new file mode 100644 index 00000000000..5041e308e3a Binary files /dev/null and b/Mage.Client/src/main/resources/flags/catalonia.png differ diff --git a/Mage.Client/src/main/resources/flags/cc.png b/Mage.Client/src/main/resources/flags/cc.png new file mode 100644 index 00000000000..aed3d3b4e44 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cc.png differ diff --git a/Mage.Client/src/main/resources/flags/cd.png b/Mage.Client/src/main/resources/flags/cd.png new file mode 100644 index 00000000000..5e489424884 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cd.png differ diff --git a/Mage.Client/src/main/resources/flags/cf.png b/Mage.Client/src/main/resources/flags/cf.png new file mode 100644 index 00000000000..da687bdce92 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cf.png differ diff --git a/Mage.Client/src/main/resources/flags/cg.png b/Mage.Client/src/main/resources/flags/cg.png new file mode 100644 index 00000000000..a859792ef32 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cg.png differ diff --git a/Mage.Client/src/main/resources/flags/ch.png b/Mage.Client/src/main/resources/flags/ch.png new file mode 100644 index 00000000000..242ec01aaf5 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ch.png differ diff --git a/Mage.Client/src/main/resources/flags/ci.png b/Mage.Client/src/main/resources/flags/ci.png new file mode 100644 index 00000000000..3f2c62eb4d7 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ci.png differ diff --git a/Mage.Client/src/main/resources/flags/ck.png b/Mage.Client/src/main/resources/flags/ck.png new file mode 100644 index 00000000000..746d3d6f758 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ck.png differ diff --git a/Mage.Client/src/main/resources/flags/cl.png b/Mage.Client/src/main/resources/flags/cl.png new file mode 100644 index 00000000000..29c6d61bd4f Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cl.png differ diff --git a/Mage.Client/src/main/resources/flags/cm.png b/Mage.Client/src/main/resources/flags/cm.png new file mode 100644 index 00000000000..f65c5bd5a79 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cm.png differ diff --git a/Mage.Client/src/main/resources/flags/cn.png b/Mage.Client/src/main/resources/flags/cn.png new file mode 100644 index 00000000000..89144146219 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cn.png differ diff --git a/Mage.Client/src/main/resources/flags/co.png b/Mage.Client/src/main/resources/flags/co.png new file mode 100644 index 00000000000..a118ff4a146 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/co.png differ diff --git a/Mage.Client/src/main/resources/flags/cr.png b/Mage.Client/src/main/resources/flags/cr.png new file mode 100644 index 00000000000..c7a37317940 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cr.png differ diff --git a/Mage.Client/src/main/resources/flags/cs.png b/Mage.Client/src/main/resources/flags/cs.png new file mode 100644 index 00000000000..8254790ca72 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cs.png differ diff --git a/Mage.Client/src/main/resources/flags/cu.png b/Mage.Client/src/main/resources/flags/cu.png new file mode 100644 index 00000000000..083f1d611c9 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cu.png differ diff --git a/Mage.Client/src/main/resources/flags/cv.png b/Mage.Client/src/main/resources/flags/cv.png new file mode 100644 index 00000000000..a63f7eaf63c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cv.png differ diff --git a/Mage.Client/src/main/resources/flags/cx.png b/Mage.Client/src/main/resources/flags/cx.png new file mode 100644 index 00000000000..48e31adbf4c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cx.png differ diff --git a/Mage.Client/src/main/resources/flags/cy.png b/Mage.Client/src/main/resources/flags/cy.png new file mode 100644 index 00000000000..5b1ad6c0788 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cy.png differ diff --git a/Mage.Client/src/main/resources/flags/cz.png b/Mage.Client/src/main/resources/flags/cz.png new file mode 100644 index 00000000000..c8403dd21fd Binary files /dev/null and b/Mage.Client/src/main/resources/flags/cz.png differ diff --git a/Mage.Client/src/main/resources/flags/de.png b/Mage.Client/src/main/resources/flags/de.png new file mode 100644 index 00000000000..ac4a9773627 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/de.png differ diff --git a/Mage.Client/src/main/resources/flags/dj.png b/Mage.Client/src/main/resources/flags/dj.png new file mode 100644 index 00000000000..582af364f8a Binary files /dev/null and b/Mage.Client/src/main/resources/flags/dj.png differ diff --git a/Mage.Client/src/main/resources/flags/dk.png b/Mage.Client/src/main/resources/flags/dk.png new file mode 100644 index 00000000000..e2993d3c59a Binary files /dev/null and b/Mage.Client/src/main/resources/flags/dk.png differ diff --git a/Mage.Client/src/main/resources/flags/dm.png b/Mage.Client/src/main/resources/flags/dm.png new file mode 100644 index 00000000000..5fbffcba3cb Binary files /dev/null and b/Mage.Client/src/main/resources/flags/dm.png differ diff --git a/Mage.Client/src/main/resources/flags/do.png b/Mage.Client/src/main/resources/flags/do.png new file mode 100644 index 00000000000..5a04932d879 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/do.png differ diff --git a/Mage.Client/src/main/resources/flags/dz.png b/Mage.Client/src/main/resources/flags/dz.png new file mode 100644 index 00000000000..335c2391d39 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/dz.png differ diff --git a/Mage.Client/src/main/resources/flags/ec.png b/Mage.Client/src/main/resources/flags/ec.png new file mode 100644 index 00000000000..0caa0b1e785 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ec.png differ diff --git a/Mage.Client/src/main/resources/flags/ee.png b/Mage.Client/src/main/resources/flags/ee.png new file mode 100644 index 00000000000..0c82efb7dde Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ee.png differ diff --git a/Mage.Client/src/main/resources/flags/eg.png b/Mage.Client/src/main/resources/flags/eg.png new file mode 100644 index 00000000000..8a3f7a10b57 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/eg.png differ diff --git a/Mage.Client/src/main/resources/flags/eh.png b/Mage.Client/src/main/resources/flags/eh.png new file mode 100644 index 00000000000..90a1195b47a Binary files /dev/null and b/Mage.Client/src/main/resources/flags/eh.png differ diff --git a/Mage.Client/src/main/resources/flags/england.png b/Mage.Client/src/main/resources/flags/england.png new file mode 100644 index 00000000000..3a7311d5617 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/england.png differ diff --git a/Mage.Client/src/main/resources/flags/er.png b/Mage.Client/src/main/resources/flags/er.png new file mode 100644 index 00000000000..13065ae99cc Binary files /dev/null and b/Mage.Client/src/main/resources/flags/er.png differ diff --git a/Mage.Client/src/main/resources/flags/es.png b/Mage.Client/src/main/resources/flags/es.png new file mode 100644 index 00000000000..c2de2d7111e Binary files /dev/null and b/Mage.Client/src/main/resources/flags/es.png differ diff --git a/Mage.Client/src/main/resources/flags/et.png b/Mage.Client/src/main/resources/flags/et.png new file mode 100644 index 00000000000..2e893fa056c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/et.png differ diff --git a/Mage.Client/src/main/resources/flags/europeanunion.png b/Mage.Client/src/main/resources/flags/europeanunion.png new file mode 100644 index 00000000000..d6d87115808 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/europeanunion.png differ diff --git a/Mage.Client/src/main/resources/flags/fi.png b/Mage.Client/src/main/resources/flags/fi.png new file mode 100644 index 00000000000..14ec091b802 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/fi.png differ diff --git a/Mage.Client/src/main/resources/flags/fj.png b/Mage.Client/src/main/resources/flags/fj.png new file mode 100644 index 00000000000..cee998892eb Binary files /dev/null and b/Mage.Client/src/main/resources/flags/fj.png differ diff --git a/Mage.Client/src/main/resources/flags/fk.png b/Mage.Client/src/main/resources/flags/fk.png new file mode 100644 index 00000000000..ceaeb27decb Binary files /dev/null and b/Mage.Client/src/main/resources/flags/fk.png differ diff --git a/Mage.Client/src/main/resources/flags/fm.png b/Mage.Client/src/main/resources/flags/fm.png new file mode 100644 index 00000000000..066bb247389 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/fm.png differ diff --git a/Mage.Client/src/main/resources/flags/fo.png b/Mage.Client/src/main/resources/flags/fo.png new file mode 100644 index 00000000000..cbceb809eb9 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/fo.png differ diff --git a/Mage.Client/src/main/resources/flags/fr.png b/Mage.Client/src/main/resources/flags/fr.png new file mode 100644 index 00000000000..8332c4ec23c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/fr.png differ diff --git a/Mage.Client/src/main/resources/flags/ga.png b/Mage.Client/src/main/resources/flags/ga.png new file mode 100644 index 00000000000..0e0d434363a Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ga.png differ diff --git a/Mage.Client/src/main/resources/flags/gb.png b/Mage.Client/src/main/resources/flags/gb.png new file mode 100644 index 00000000000..ff701e19f6d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gb.png differ diff --git a/Mage.Client/src/main/resources/flags/gd.png b/Mage.Client/src/main/resources/flags/gd.png new file mode 100644 index 00000000000..9ab57f5489b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gd.png differ diff --git a/Mage.Client/src/main/resources/flags/ge.png b/Mage.Client/src/main/resources/flags/ge.png new file mode 100644 index 00000000000..728d97078df Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ge.png differ diff --git a/Mage.Client/src/main/resources/flags/gf.png b/Mage.Client/src/main/resources/flags/gf.png new file mode 100644 index 00000000000..8332c4ec23c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gf.png differ diff --git a/Mage.Client/src/main/resources/flags/gh.png b/Mage.Client/src/main/resources/flags/gh.png new file mode 100644 index 00000000000..4e2f8965914 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gh.png differ diff --git a/Mage.Client/src/main/resources/flags/gi.png b/Mage.Client/src/main/resources/flags/gi.png new file mode 100644 index 00000000000..e76797f62fe Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gi.png differ diff --git a/Mage.Client/src/main/resources/flags/gl.png b/Mage.Client/src/main/resources/flags/gl.png new file mode 100644 index 00000000000..ef12a73bf96 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gl.png differ diff --git a/Mage.Client/src/main/resources/flags/gm.png b/Mage.Client/src/main/resources/flags/gm.png new file mode 100644 index 00000000000..0720b667aff Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gm.png differ diff --git a/Mage.Client/src/main/resources/flags/gn.png b/Mage.Client/src/main/resources/flags/gn.png new file mode 100644 index 00000000000..ea660b01fae Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gn.png differ diff --git a/Mage.Client/src/main/resources/flags/gp.png b/Mage.Client/src/main/resources/flags/gp.png new file mode 100644 index 00000000000..dbb086d0012 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gp.png differ diff --git a/Mage.Client/src/main/resources/flags/gq.png b/Mage.Client/src/main/resources/flags/gq.png new file mode 100644 index 00000000000..ebe20a28de0 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gq.png differ diff --git a/Mage.Client/src/main/resources/flags/gr.png b/Mage.Client/src/main/resources/flags/gr.png new file mode 100644 index 00000000000..8651ade7cbe Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gr.png differ diff --git a/Mage.Client/src/main/resources/flags/gs.png b/Mage.Client/src/main/resources/flags/gs.png new file mode 100644 index 00000000000..7ef0bf598d9 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gs.png differ diff --git a/Mage.Client/src/main/resources/flags/gt.png b/Mage.Client/src/main/resources/flags/gt.png new file mode 100644 index 00000000000..c43a70d3642 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gt.png differ diff --git a/Mage.Client/src/main/resources/flags/gu.png b/Mage.Client/src/main/resources/flags/gu.png new file mode 100644 index 00000000000..92f37c05330 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gu.png differ diff --git a/Mage.Client/src/main/resources/flags/gw.png b/Mage.Client/src/main/resources/flags/gw.png new file mode 100644 index 00000000000..b37bcf06bf2 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gw.png differ diff --git a/Mage.Client/src/main/resources/flags/gy.png b/Mage.Client/src/main/resources/flags/gy.png new file mode 100644 index 00000000000..22cbe2f5914 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/gy.png differ diff --git a/Mage.Client/src/main/resources/flags/hk.png b/Mage.Client/src/main/resources/flags/hk.png new file mode 100644 index 00000000000..d5c380ca9d8 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/hk.png differ diff --git a/Mage.Client/src/main/resources/flags/hm.png b/Mage.Client/src/main/resources/flags/hm.png new file mode 100644 index 00000000000..a01389a745d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/hm.png differ diff --git a/Mage.Client/src/main/resources/flags/hn.png b/Mage.Client/src/main/resources/flags/hn.png new file mode 100644 index 00000000000..96f838859fd Binary files /dev/null and b/Mage.Client/src/main/resources/flags/hn.png differ diff --git a/Mage.Client/src/main/resources/flags/hr.png b/Mage.Client/src/main/resources/flags/hr.png new file mode 100644 index 00000000000..696b515460d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/hr.png differ diff --git a/Mage.Client/src/main/resources/flags/ht.png b/Mage.Client/src/main/resources/flags/ht.png new file mode 100644 index 00000000000..416052af772 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ht.png differ diff --git a/Mage.Client/src/main/resources/flags/hu.png b/Mage.Client/src/main/resources/flags/hu.png new file mode 100644 index 00000000000..7baafe44ddc Binary files /dev/null and b/Mage.Client/src/main/resources/flags/hu.png differ diff --git a/Mage.Client/src/main/resources/flags/id.png b/Mage.Client/src/main/resources/flags/id.png new file mode 100644 index 00000000000..c6bc0fafac7 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/id.png differ diff --git a/Mage.Client/src/main/resources/flags/ie.png b/Mage.Client/src/main/resources/flags/ie.png new file mode 100644 index 00000000000..26baa31e182 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ie.png differ diff --git a/Mage.Client/src/main/resources/flags/il.png b/Mage.Client/src/main/resources/flags/il.png new file mode 100644 index 00000000000..2ca772d0b79 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/il.png differ diff --git a/Mage.Client/src/main/resources/flags/in.png b/Mage.Client/src/main/resources/flags/in.png new file mode 100644 index 00000000000..e4d7e81a98d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/in.png differ diff --git a/Mage.Client/src/main/resources/flags/io.png b/Mage.Client/src/main/resources/flags/io.png new file mode 100644 index 00000000000..3e74b6a3164 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/io.png differ diff --git a/Mage.Client/src/main/resources/flags/iq.png b/Mage.Client/src/main/resources/flags/iq.png new file mode 100644 index 00000000000..878a351403a Binary files /dev/null and b/Mage.Client/src/main/resources/flags/iq.png differ diff --git a/Mage.Client/src/main/resources/flags/ir.png b/Mage.Client/src/main/resources/flags/ir.png new file mode 100644 index 00000000000..c5fd136aee5 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ir.png differ diff --git a/Mage.Client/src/main/resources/flags/is.png b/Mage.Client/src/main/resources/flags/is.png new file mode 100644 index 00000000000..b8f6d0f0667 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/is.png differ diff --git a/Mage.Client/src/main/resources/flags/it.png b/Mage.Client/src/main/resources/flags/it.png new file mode 100644 index 00000000000..89692f74f05 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/it.png differ diff --git a/Mage.Client/src/main/resources/flags/jm.png b/Mage.Client/src/main/resources/flags/jm.png new file mode 100644 index 00000000000..7be119e03d2 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/jm.png differ diff --git a/Mage.Client/src/main/resources/flags/jo.png b/Mage.Client/src/main/resources/flags/jo.png new file mode 100644 index 00000000000..11bd4972b6d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/jo.png differ diff --git a/Mage.Client/src/main/resources/flags/jp.png b/Mage.Client/src/main/resources/flags/jp.png new file mode 100644 index 00000000000..325fbad3ffd Binary files /dev/null and b/Mage.Client/src/main/resources/flags/jp.png differ diff --git a/Mage.Client/src/main/resources/flags/ke.png b/Mage.Client/src/main/resources/flags/ke.png new file mode 100644 index 00000000000..51879adf17c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ke.png differ diff --git a/Mage.Client/src/main/resources/flags/kg.png b/Mage.Client/src/main/resources/flags/kg.png new file mode 100644 index 00000000000..0a818f67ea3 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/kg.png differ diff --git a/Mage.Client/src/main/resources/flags/kh.png b/Mage.Client/src/main/resources/flags/kh.png new file mode 100644 index 00000000000..30f6bb1b9b6 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/kh.png differ diff --git a/Mage.Client/src/main/resources/flags/ki.png b/Mage.Client/src/main/resources/flags/ki.png new file mode 100644 index 00000000000..2dcce4b33ff Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ki.png differ diff --git a/Mage.Client/src/main/resources/flags/km.png b/Mage.Client/src/main/resources/flags/km.png new file mode 100644 index 00000000000..812b2f56c5a Binary files /dev/null and b/Mage.Client/src/main/resources/flags/km.png differ diff --git a/Mage.Client/src/main/resources/flags/kn.png b/Mage.Client/src/main/resources/flags/kn.png new file mode 100644 index 00000000000..febd5b486f3 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/kn.png differ diff --git a/Mage.Client/src/main/resources/flags/kp.png b/Mage.Client/src/main/resources/flags/kp.png new file mode 100644 index 00000000000..d3d509aa874 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/kp.png differ diff --git a/Mage.Client/src/main/resources/flags/kr.png b/Mage.Client/src/main/resources/flags/kr.png new file mode 100644 index 00000000000..9c0a78eb942 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/kr.png differ diff --git a/Mage.Client/src/main/resources/flags/kw.png b/Mage.Client/src/main/resources/flags/kw.png new file mode 100644 index 00000000000..96546da328a Binary files /dev/null and b/Mage.Client/src/main/resources/flags/kw.png differ diff --git a/Mage.Client/src/main/resources/flags/ky.png b/Mage.Client/src/main/resources/flags/ky.png new file mode 100644 index 00000000000..15c5f8e4775 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ky.png differ diff --git a/Mage.Client/src/main/resources/flags/kz.png b/Mage.Client/src/main/resources/flags/kz.png new file mode 100644 index 00000000000..45a8c887424 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/kz.png differ diff --git a/Mage.Client/src/main/resources/flags/la.png b/Mage.Client/src/main/resources/flags/la.png new file mode 100644 index 00000000000..e28acd018a2 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/la.png differ diff --git a/Mage.Client/src/main/resources/flags/lb.png b/Mage.Client/src/main/resources/flags/lb.png new file mode 100644 index 00000000000..d0d452bf868 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/lb.png differ diff --git a/Mage.Client/src/main/resources/flags/lc.png b/Mage.Client/src/main/resources/flags/lc.png new file mode 100644 index 00000000000..a47d065541b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/lc.png differ diff --git a/Mage.Client/src/main/resources/flags/li.png b/Mage.Client/src/main/resources/flags/li.png new file mode 100644 index 00000000000..6469909c013 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/li.png differ diff --git a/Mage.Client/src/main/resources/flags/lk.png b/Mage.Client/src/main/resources/flags/lk.png new file mode 100644 index 00000000000..088aad6db95 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/lk.png differ diff --git a/Mage.Client/src/main/resources/flags/lr.png b/Mage.Client/src/main/resources/flags/lr.png new file mode 100644 index 00000000000..89a5bc7e707 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/lr.png differ diff --git a/Mage.Client/src/main/resources/flags/ls.png b/Mage.Client/src/main/resources/flags/ls.png new file mode 100644 index 00000000000..33fdef101f7 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ls.png differ diff --git a/Mage.Client/src/main/resources/flags/lt.png b/Mage.Client/src/main/resources/flags/lt.png new file mode 100644 index 00000000000..c8ef0da0919 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/lt.png differ diff --git a/Mage.Client/src/main/resources/flags/lu.png b/Mage.Client/src/main/resources/flags/lu.png new file mode 100644 index 00000000000..4cabba98ae7 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/lu.png differ diff --git a/Mage.Client/src/main/resources/flags/lv.png b/Mage.Client/src/main/resources/flags/lv.png new file mode 100644 index 00000000000..49b69981085 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/lv.png differ diff --git a/Mage.Client/src/main/resources/flags/ly.png b/Mage.Client/src/main/resources/flags/ly.png new file mode 100644 index 00000000000..b163a9f8a06 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ly.png differ diff --git a/Mage.Client/src/main/resources/flags/ma.png b/Mage.Client/src/main/resources/flags/ma.png new file mode 100644 index 00000000000..f386770280b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ma.png differ diff --git a/Mage.Client/src/main/resources/flags/mc.png b/Mage.Client/src/main/resources/flags/mc.png new file mode 100644 index 00000000000..1aa830f121a Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mc.png differ diff --git a/Mage.Client/src/main/resources/flags/md.png b/Mage.Client/src/main/resources/flags/md.png new file mode 100644 index 00000000000..4e92c189044 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/md.png differ diff --git a/Mage.Client/src/main/resources/flags/me.png b/Mage.Client/src/main/resources/flags/me.png new file mode 100644 index 00000000000..ac7253558ab Binary files /dev/null and b/Mage.Client/src/main/resources/flags/me.png differ diff --git a/Mage.Client/src/main/resources/flags/mg.png b/Mage.Client/src/main/resources/flags/mg.png new file mode 100644 index 00000000000..d2715b3d0e1 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mg.png differ diff --git a/Mage.Client/src/main/resources/flags/mh.png b/Mage.Client/src/main/resources/flags/mh.png new file mode 100644 index 00000000000..fb523a8c39d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mh.png differ diff --git a/Mage.Client/src/main/resources/flags/mk.png b/Mage.Client/src/main/resources/flags/mk.png new file mode 100644 index 00000000000..db173aaff21 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mk.png differ diff --git a/Mage.Client/src/main/resources/flags/ml.png b/Mage.Client/src/main/resources/flags/ml.png new file mode 100644 index 00000000000..2cec8ba440b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ml.png differ diff --git a/Mage.Client/src/main/resources/flags/mm.png b/Mage.Client/src/main/resources/flags/mm.png new file mode 100644 index 00000000000..f464f67ffb4 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mm.png differ diff --git a/Mage.Client/src/main/resources/flags/mn.png b/Mage.Client/src/main/resources/flags/mn.png new file mode 100644 index 00000000000..9396355db45 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mn.png differ diff --git a/Mage.Client/src/main/resources/flags/mo.png b/Mage.Client/src/main/resources/flags/mo.png new file mode 100644 index 00000000000..deb801dda24 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mo.png differ diff --git a/Mage.Client/src/main/resources/flags/mp.png b/Mage.Client/src/main/resources/flags/mp.png new file mode 100644 index 00000000000..298d588b14b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mp.png differ diff --git a/Mage.Client/src/main/resources/flags/mq.png b/Mage.Client/src/main/resources/flags/mq.png new file mode 100644 index 00000000000..010143b3867 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mq.png differ diff --git a/Mage.Client/src/main/resources/flags/mr.png b/Mage.Client/src/main/resources/flags/mr.png new file mode 100644 index 00000000000..319546b1008 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mr.png differ diff --git a/Mage.Client/src/main/resources/flags/ms.png b/Mage.Client/src/main/resources/flags/ms.png new file mode 100644 index 00000000000..d4cbb433d8f Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ms.png differ diff --git a/Mage.Client/src/main/resources/flags/mt.png b/Mage.Client/src/main/resources/flags/mt.png new file mode 100644 index 00000000000..00af94871de Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mt.png differ diff --git a/Mage.Client/src/main/resources/flags/mu.png b/Mage.Client/src/main/resources/flags/mu.png new file mode 100644 index 00000000000..b7fdce1bdd7 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mu.png differ diff --git a/Mage.Client/src/main/resources/flags/mv.png b/Mage.Client/src/main/resources/flags/mv.png new file mode 100644 index 00000000000..5073d9ec47c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mv.png differ diff --git a/Mage.Client/src/main/resources/flags/mw.png b/Mage.Client/src/main/resources/flags/mw.png new file mode 100644 index 00000000000..13886e9f8bf Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mw.png differ diff --git a/Mage.Client/src/main/resources/flags/mx.png b/Mage.Client/src/main/resources/flags/mx.png new file mode 100644 index 00000000000..5bc58ab3e35 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mx.png differ diff --git a/Mage.Client/src/main/resources/flags/my.png b/Mage.Client/src/main/resources/flags/my.png new file mode 100644 index 00000000000..9034cbab2c0 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/my.png differ diff --git a/Mage.Client/src/main/resources/flags/mz.png b/Mage.Client/src/main/resources/flags/mz.png new file mode 100644 index 00000000000..76405e063d4 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/mz.png differ diff --git a/Mage.Client/src/main/resources/flags/na.png b/Mage.Client/src/main/resources/flags/na.png new file mode 100644 index 00000000000..63358c67df9 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/na.png differ diff --git a/Mage.Client/src/main/resources/flags/nc.png b/Mage.Client/src/main/resources/flags/nc.png new file mode 100644 index 00000000000..2cad2837823 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/nc.png differ diff --git a/Mage.Client/src/main/resources/flags/ne.png b/Mage.Client/src/main/resources/flags/ne.png new file mode 100644 index 00000000000..d85f424f38d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ne.png differ diff --git a/Mage.Client/src/main/resources/flags/nf.png b/Mage.Client/src/main/resources/flags/nf.png new file mode 100644 index 00000000000..f9bcdda12ca Binary files /dev/null and b/Mage.Client/src/main/resources/flags/nf.png differ diff --git a/Mage.Client/src/main/resources/flags/ng.png b/Mage.Client/src/main/resources/flags/ng.png new file mode 100644 index 00000000000..3eea2e02075 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ng.png differ diff --git a/Mage.Client/src/main/resources/flags/ni.png b/Mage.Client/src/main/resources/flags/ni.png new file mode 100644 index 00000000000..3969aaaaee4 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ni.png differ diff --git a/Mage.Client/src/main/resources/flags/nl.png b/Mage.Client/src/main/resources/flags/nl.png new file mode 100644 index 00000000000..fe44791e32b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/nl.png differ diff --git a/Mage.Client/src/main/resources/flags/no.png b/Mage.Client/src/main/resources/flags/no.png new file mode 100644 index 00000000000..160b6b5b79d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/no.png differ diff --git a/Mage.Client/src/main/resources/flags/np.png b/Mage.Client/src/main/resources/flags/np.png new file mode 100644 index 00000000000..aeb058b7ea8 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/np.png differ diff --git a/Mage.Client/src/main/resources/flags/nr.png b/Mage.Client/src/main/resources/flags/nr.png new file mode 100644 index 00000000000..705fc337ccd Binary files /dev/null and b/Mage.Client/src/main/resources/flags/nr.png differ diff --git a/Mage.Client/src/main/resources/flags/nu.png b/Mage.Client/src/main/resources/flags/nu.png new file mode 100644 index 00000000000..c3ce4aedda9 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/nu.png differ diff --git a/Mage.Client/src/main/resources/flags/nz.png b/Mage.Client/src/main/resources/flags/nz.png new file mode 100644 index 00000000000..10d6306d174 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/nz.png differ diff --git a/Mage.Client/src/main/resources/flags/om.png b/Mage.Client/src/main/resources/flags/om.png new file mode 100644 index 00000000000..2ffba7e8c43 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/om.png differ diff --git a/Mage.Client/src/main/resources/flags/pa.png b/Mage.Client/src/main/resources/flags/pa.png new file mode 100644 index 00000000000..9b2ee9a7809 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/pa.png differ diff --git a/Mage.Client/src/main/resources/flags/pe.png b/Mage.Client/src/main/resources/flags/pe.png new file mode 100644 index 00000000000..62a04977fb2 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/pe.png differ diff --git a/Mage.Client/src/main/resources/flags/pf.png b/Mage.Client/src/main/resources/flags/pf.png new file mode 100644 index 00000000000..771a0f65225 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/pf.png differ diff --git a/Mage.Client/src/main/resources/flags/pg.png b/Mage.Client/src/main/resources/flags/pg.png new file mode 100644 index 00000000000..10d6233496c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/pg.png differ diff --git a/Mage.Client/src/main/resources/flags/ph.png b/Mage.Client/src/main/resources/flags/ph.png new file mode 100644 index 00000000000..b89e15935d9 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ph.png differ diff --git a/Mage.Client/src/main/resources/flags/pk.png b/Mage.Client/src/main/resources/flags/pk.png new file mode 100644 index 00000000000..e9df70ca4d6 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/pk.png differ diff --git a/Mage.Client/src/main/resources/flags/pl.png b/Mage.Client/src/main/resources/flags/pl.png new file mode 100644 index 00000000000..d413d010b5b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/pl.png differ diff --git a/Mage.Client/src/main/resources/flags/pm.png b/Mage.Client/src/main/resources/flags/pm.png new file mode 100644 index 00000000000..ba91d2c7a0d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/pm.png differ diff --git a/Mage.Client/src/main/resources/flags/pn.png b/Mage.Client/src/main/resources/flags/pn.png new file mode 100644 index 00000000000..aa9344f575b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/pn.png differ diff --git a/Mage.Client/src/main/resources/flags/pr.png b/Mage.Client/src/main/resources/flags/pr.png new file mode 100644 index 00000000000..82d9130da45 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/pr.png differ diff --git a/Mage.Client/src/main/resources/flags/ps.png b/Mage.Client/src/main/resources/flags/ps.png new file mode 100644 index 00000000000..f5f547762ed Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ps.png differ diff --git a/Mage.Client/src/main/resources/flags/pt.png b/Mage.Client/src/main/resources/flags/pt.png new file mode 100644 index 00000000000..ece79801506 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/pt.png differ diff --git a/Mage.Client/src/main/resources/flags/pw.png b/Mage.Client/src/main/resources/flags/pw.png new file mode 100644 index 00000000000..6178b254a5d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/pw.png differ diff --git a/Mage.Client/src/main/resources/flags/py.png b/Mage.Client/src/main/resources/flags/py.png new file mode 100644 index 00000000000..cb8723c0640 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/py.png differ diff --git a/Mage.Client/src/main/resources/flags/qa.png b/Mage.Client/src/main/resources/flags/qa.png new file mode 100644 index 00000000000..ed4c621fa71 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/qa.png differ diff --git a/Mage.Client/src/main/resources/flags/re.png b/Mage.Client/src/main/resources/flags/re.png new file mode 100644 index 00000000000..8332c4ec23c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/re.png differ diff --git a/Mage.Client/src/main/resources/flags/ro.png b/Mage.Client/src/main/resources/flags/ro.png new file mode 100644 index 00000000000..57e74a6510d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ro.png differ diff --git a/Mage.Client/src/main/resources/flags/rs.png b/Mage.Client/src/main/resources/flags/rs.png new file mode 100644 index 00000000000..9439a5b605d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/rs.png differ diff --git a/Mage.Client/src/main/resources/flags/ru.png b/Mage.Client/src/main/resources/flags/ru.png new file mode 100644 index 00000000000..47da4214fd9 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ru.png differ diff --git a/Mage.Client/src/main/resources/flags/rw.png b/Mage.Client/src/main/resources/flags/rw.png new file mode 100644 index 00000000000..535649178a8 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/rw.png differ diff --git a/Mage.Client/src/main/resources/flags/sa.png b/Mage.Client/src/main/resources/flags/sa.png new file mode 100644 index 00000000000..b4641c7e8b0 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sa.png differ diff --git a/Mage.Client/src/main/resources/flags/sb.png b/Mage.Client/src/main/resources/flags/sb.png new file mode 100644 index 00000000000..a9937ccf091 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sb.png differ diff --git a/Mage.Client/src/main/resources/flags/sc.png b/Mage.Client/src/main/resources/flags/sc.png new file mode 100644 index 00000000000..39ee37184e0 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sc.png differ diff --git a/Mage.Client/src/main/resources/flags/scotland.png b/Mage.Client/src/main/resources/flags/scotland.png new file mode 100644 index 00000000000..a0e57b4122a Binary files /dev/null and b/Mage.Client/src/main/resources/flags/scotland.png differ diff --git a/Mage.Client/src/main/resources/flags/sd.png b/Mage.Client/src/main/resources/flags/sd.png new file mode 100644 index 00000000000..eaab69eb787 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sd.png differ diff --git a/Mage.Client/src/main/resources/flags/se.png b/Mage.Client/src/main/resources/flags/se.png new file mode 100644 index 00000000000..1994653dac1 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/se.png differ diff --git a/Mage.Client/src/main/resources/flags/sg.png b/Mage.Client/src/main/resources/flags/sg.png new file mode 100644 index 00000000000..dd34d612107 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sg.png differ diff --git a/Mage.Client/src/main/resources/flags/sh.png b/Mage.Client/src/main/resources/flags/sh.png new file mode 100644 index 00000000000..4b1d2a29107 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sh.png differ diff --git a/Mage.Client/src/main/resources/flags/si.png b/Mage.Client/src/main/resources/flags/si.png new file mode 100644 index 00000000000..bb1476ff5fe Binary files /dev/null and b/Mage.Client/src/main/resources/flags/si.png differ diff --git a/Mage.Client/src/main/resources/flags/sj.png b/Mage.Client/src/main/resources/flags/sj.png new file mode 100644 index 00000000000..160b6b5b79d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sj.png differ diff --git a/Mage.Client/src/main/resources/flags/sk.png b/Mage.Client/src/main/resources/flags/sk.png new file mode 100644 index 00000000000..7ccbc8274ad Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sk.png differ diff --git a/Mage.Client/src/main/resources/flags/sl.png b/Mage.Client/src/main/resources/flags/sl.png new file mode 100644 index 00000000000..12d812d29fa Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sl.png differ diff --git a/Mage.Client/src/main/resources/flags/sm.png b/Mage.Client/src/main/resources/flags/sm.png new file mode 100644 index 00000000000..3df2fdcf8c0 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sm.png differ diff --git a/Mage.Client/src/main/resources/flags/sn.png b/Mage.Client/src/main/resources/flags/sn.png new file mode 100644 index 00000000000..eabb71db4e8 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sn.png differ diff --git a/Mage.Client/src/main/resources/flags/so.png b/Mage.Client/src/main/resources/flags/so.png new file mode 100644 index 00000000000..4a1ea4b29b3 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/so.png differ diff --git a/Mage.Client/src/main/resources/flags/sr.png b/Mage.Client/src/main/resources/flags/sr.png new file mode 100644 index 00000000000..5eff9271d28 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sr.png differ diff --git a/Mage.Client/src/main/resources/flags/st.png b/Mage.Client/src/main/resources/flags/st.png new file mode 100644 index 00000000000..2978557b19d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/st.png differ diff --git a/Mage.Client/src/main/resources/flags/sv.png b/Mage.Client/src/main/resources/flags/sv.png new file mode 100644 index 00000000000..24987990b73 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sv.png differ diff --git a/Mage.Client/src/main/resources/flags/sy.png b/Mage.Client/src/main/resources/flags/sy.png new file mode 100644 index 00000000000..f5ce30dcb79 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sy.png differ diff --git a/Mage.Client/src/main/resources/flags/sz.png b/Mage.Client/src/main/resources/flags/sz.png new file mode 100644 index 00000000000..914ee861d41 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/sz.png differ diff --git a/Mage.Client/src/main/resources/flags/tc.png b/Mage.Client/src/main/resources/flags/tc.png new file mode 100644 index 00000000000..8fc1156bec3 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tc.png differ diff --git a/Mage.Client/src/main/resources/flags/td.png b/Mage.Client/src/main/resources/flags/td.png new file mode 100644 index 00000000000..667f21fd9d5 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/td.png differ diff --git a/Mage.Client/src/main/resources/flags/tf.png b/Mage.Client/src/main/resources/flags/tf.png new file mode 100644 index 00000000000..80529a43619 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tf.png differ diff --git a/Mage.Client/src/main/resources/flags/tg.png b/Mage.Client/src/main/resources/flags/tg.png new file mode 100644 index 00000000000..3aa00ad4dfa Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tg.png differ diff --git a/Mage.Client/src/main/resources/flags/th.png b/Mage.Client/src/main/resources/flags/th.png new file mode 100644 index 00000000000..dd8ba91719b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/th.png differ diff --git a/Mage.Client/src/main/resources/flags/tj.png b/Mage.Client/src/main/resources/flags/tj.png new file mode 100644 index 00000000000..617bf6455f6 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tj.png differ diff --git a/Mage.Client/src/main/resources/flags/tk.png b/Mage.Client/src/main/resources/flags/tk.png new file mode 100644 index 00000000000..67b8c8cb519 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tk.png differ diff --git a/Mage.Client/src/main/resources/flags/tl.png b/Mage.Client/src/main/resources/flags/tl.png new file mode 100644 index 00000000000..77da181e9c5 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tl.png differ diff --git a/Mage.Client/src/main/resources/flags/tm.png b/Mage.Client/src/main/resources/flags/tm.png new file mode 100644 index 00000000000..828020ecd0f Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tm.png differ diff --git a/Mage.Client/src/main/resources/flags/tn.png b/Mage.Client/src/main/resources/flags/tn.png new file mode 100644 index 00000000000..183cdd3dc98 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tn.png differ diff --git a/Mage.Client/src/main/resources/flags/to.png b/Mage.Client/src/main/resources/flags/to.png new file mode 100644 index 00000000000..f89b8ba755f Binary files /dev/null and b/Mage.Client/src/main/resources/flags/to.png differ diff --git a/Mage.Client/src/main/resources/flags/tr.png b/Mage.Client/src/main/resources/flags/tr.png new file mode 100644 index 00000000000..be32f77e991 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tr.png differ diff --git a/Mage.Client/src/main/resources/flags/tt.png b/Mage.Client/src/main/resources/flags/tt.png new file mode 100644 index 00000000000..2a11c1e20ac Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tt.png differ diff --git a/Mage.Client/src/main/resources/flags/tv.png b/Mage.Client/src/main/resources/flags/tv.png new file mode 100644 index 00000000000..28274c5fb40 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tv.png differ diff --git a/Mage.Client/src/main/resources/flags/tw.png b/Mage.Client/src/main/resources/flags/tw.png new file mode 100644 index 00000000000..f31c654c99c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tw.png differ diff --git a/Mage.Client/src/main/resources/flags/tz.png b/Mage.Client/src/main/resources/flags/tz.png new file mode 100644 index 00000000000..c00ff796142 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/tz.png differ diff --git a/Mage.Client/src/main/resources/flags/ua.png b/Mage.Client/src/main/resources/flags/ua.png new file mode 100644 index 00000000000..09563a21941 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ua.png differ diff --git a/Mage.Client/src/main/resources/flags/ug.png b/Mage.Client/src/main/resources/flags/ug.png new file mode 100644 index 00000000000..33f4affadee Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ug.png differ diff --git a/Mage.Client/src/main/resources/flags/um.png b/Mage.Client/src/main/resources/flags/um.png new file mode 100644 index 00000000000..c1dd9654b07 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/um.png differ diff --git a/Mage.Client/src/main/resources/flags/us.png b/Mage.Client/src/main/resources/flags/us.png new file mode 100644 index 00000000000..10f451fe85c Binary files /dev/null and b/Mage.Client/src/main/resources/flags/us.png differ diff --git a/Mage.Client/src/main/resources/flags/uy.png b/Mage.Client/src/main/resources/flags/uy.png new file mode 100644 index 00000000000..31d948a067f Binary files /dev/null and b/Mage.Client/src/main/resources/flags/uy.png differ diff --git a/Mage.Client/src/main/resources/flags/uz.png b/Mage.Client/src/main/resources/flags/uz.png new file mode 100644 index 00000000000..fef5dc1709d Binary files /dev/null and b/Mage.Client/src/main/resources/flags/uz.png differ diff --git a/Mage.Client/src/main/resources/flags/va.png b/Mage.Client/src/main/resources/flags/va.png new file mode 100644 index 00000000000..b31eaf225d6 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/va.png differ diff --git a/Mage.Client/src/main/resources/flags/vc.png b/Mage.Client/src/main/resources/flags/vc.png new file mode 100644 index 00000000000..8fa17b0612b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/vc.png differ diff --git a/Mage.Client/src/main/resources/flags/ve.png b/Mage.Client/src/main/resources/flags/ve.png new file mode 100644 index 00000000000..00c90f9aff0 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ve.png differ diff --git a/Mage.Client/src/main/resources/flags/vg.png b/Mage.Client/src/main/resources/flags/vg.png new file mode 100644 index 00000000000..41569079865 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/vg.png differ diff --git a/Mage.Client/src/main/resources/flags/vi.png b/Mage.Client/src/main/resources/flags/vi.png new file mode 100644 index 00000000000..ed26915a323 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/vi.png differ diff --git a/Mage.Client/src/main/resources/flags/vn.png b/Mage.Client/src/main/resources/flags/vn.png new file mode 100644 index 00000000000..ec7cd48a346 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/vn.png differ diff --git a/Mage.Client/src/main/resources/flags/vu.png b/Mage.Client/src/main/resources/flags/vu.png new file mode 100644 index 00000000000..b3397bc63d7 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/vu.png differ diff --git a/Mage.Client/src/main/resources/flags/wales.png b/Mage.Client/src/main/resources/flags/wales.png new file mode 100644 index 00000000000..e0d7cee1107 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/wales.png differ diff --git a/Mage.Client/src/main/resources/flags/wf.png b/Mage.Client/src/main/resources/flags/wf.png new file mode 100644 index 00000000000..9f9558734f0 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/wf.png differ diff --git a/Mage.Client/src/main/resources/flags/world.png b/Mage.Client/src/main/resources/flags/world.png new file mode 100644 index 00000000000..bb27b2c1cc7 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/world.png differ diff --git a/Mage.Client/src/main/resources/flags/ws.png b/Mage.Client/src/main/resources/flags/ws.png new file mode 100644 index 00000000000..c16950802ea Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ws.png differ diff --git a/Mage.Client/src/main/resources/flags/ye.png b/Mage.Client/src/main/resources/flags/ye.png new file mode 100644 index 00000000000..468dfad0386 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/ye.png differ diff --git a/Mage.Client/src/main/resources/flags/yt.png b/Mage.Client/src/main/resources/flags/yt.png new file mode 100644 index 00000000000..c298f378bee Binary files /dev/null and b/Mage.Client/src/main/resources/flags/yt.png differ diff --git a/Mage.Client/src/main/resources/flags/za.png b/Mage.Client/src/main/resources/flags/za.png new file mode 100644 index 00000000000..57c58e2119f Binary files /dev/null and b/Mage.Client/src/main/resources/flags/za.png differ diff --git a/Mage.Client/src/main/resources/flags/zm.png b/Mage.Client/src/main/resources/flags/zm.png new file mode 100644 index 00000000000..c25b07beef8 Binary files /dev/null and b/Mage.Client/src/main/resources/flags/zm.png differ diff --git a/Mage.Client/src/main/resources/flags/zw.png b/Mage.Client/src/main/resources/flags/zw.png new file mode 100644 index 00000000000..53c97259b9b Binary files /dev/null and b/Mage.Client/src/main/resources/flags/zw.png differ diff --git a/Mage.Common/src/mage/remote/Connection.java b/Mage.Common/src/mage/remote/Connection.java index b93e28ebd3c..21e51faf794 100644 --- a/Mage.Common/src/mage/remote/Connection.java +++ b/Mage.Common/src/mage/remote/Connection.java @@ -57,6 +57,8 @@ public class Connection { private int avatarId; private boolean showAbilityPickerForced; private boolean allowRequestShowHandCards; + private boolean confirmEmptyManaPool; + private String flagName; private UserSkipPrioritySteps userSkipPrioritySteps; private static final String serialization = "?serializationtype=jboss"; @@ -242,6 +244,16 @@ public class Connection { public void setAllowRequestShowHandCards(boolean allowRequestShowHandCards) { this.allowRequestShowHandCards = allowRequestShowHandCards; } + + public boolean confirmEmptyManaPool() { + return confirmEmptyManaPool; + } + + public void setConfirmEmptyManaPool(boolean confirmEmptyManaPool) { + this.confirmEmptyManaPool = confirmEmptyManaPool; + } + + public UserSkipPrioritySteps getUserSkipPrioritySteps() { return userSkipPrioritySteps; } @@ -258,4 +270,12 @@ public class Connection { this.forceDBComparison = forceDBComparison; } + public String getFlagName() { + return flagName; + } + + public void setFlagName(String flagName) { + this.flagName = flagName; + } + } diff --git a/Mage.Common/src/mage/remote/SessionImpl.java b/Mage.Common/src/mage/remote/SessionImpl.java index 2263f5152a7..cf08c5faa91 100644 --- a/Mage.Common/src/mage/remote/SessionImpl.java +++ b/Mage.Common/src/mage/remote/SessionImpl.java @@ -1,3 +1,4 @@ +<<<<<<< HEAD ///* //* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. //* @@ -1352,6 +1353,1111 @@ // // @Override // public boolean endUserSession(String userSessionId) { +======= +/* +* 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.remote; + +import java.io.IOException; +import java.lang.reflect.UndeclaredThrowableException; +import java.net.Authenticator; +import java.net.ConnectException; +import java.net.MalformedURLException; +import java.net.PasswordAuthentication; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import mage.MageException; +import mage.cards.decks.DeckCardLists; +import mage.cards.decks.InvalidDeckException; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; +import mage.cards.repository.ExpansionInfo; +import mage.cards.repository.ExpansionRepository; +import mage.constants.Constants.SessionState; +import mage.constants.ManaType; +import mage.constants.PlayerAction; +import mage.game.GameException; +import mage.game.match.MatchOptions; +import mage.game.tournament.TournamentOptions; +import mage.interfaces.MageClient; +import mage.interfaces.MageServer; +import mage.interfaces.ServerState; +import mage.interfaces.callback.ClientCallback; +import mage.utils.CompressUtil; +import mage.players.net.UserSkipPrioritySteps; +import mage.view.DraftPickView; +import mage.view.GameTypeView; +import mage.view.MatchView; +import mage.view.RoomUsersView; +import mage.view.TableView; +import mage.view.TournamentTypeView; +import mage.view.TournamentView; +import mage.view.UserDataView; +import mage.view.UserView; +import org.apache.log4j.Logger; +import org.jboss.remoting.CannotConnectException; +import org.jboss.remoting.Client; +import org.jboss.remoting.ConnectionListener; +import org.jboss.remoting.ConnectionValidator; +import org.jboss.remoting.InvocationFailureException; +import org.jboss.remoting.InvokerLocator; +import org.jboss.remoting.Remoting; +import org.jboss.remoting.callback.Callback; +import org.jboss.remoting.callback.HandleCallbackException; +import org.jboss.remoting.callback.InvokerCallbackHandler; +import org.jboss.remoting.transport.bisocket.Bisocket; +import org.jboss.remoting.transport.socket.SocketWrapper; +import org.jboss.remoting.transporter.TransporterClient; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class SessionImpl implements Session { + + private static final Logger logger = Logger.getLogger(SessionImpl.class); + + private final MageClient client; + + private String sessionId; + private MageServer server; + private Client callbackClient; + private CallbackHandler callbackHandler; + private ServerState serverState; + private SessionState sessionState = SessionState.DISCONNECTED; + private Connection connection; + private final static int PING_CYCLES = 10; + private final LinkedList pingTime = new LinkedList<>(); + private String pingInfo = ""; + private static boolean debugMode = false; + + private boolean canceled = false; + + static { + debugMode = System.getProperty("debug.mage") != null; + } + + public SessionImpl(MageClient client) { + this.client = client; + } + + @Override + public String getSessionId() { + return sessionId; + } + + @Override + public synchronized boolean connect(Connection connection) { + if (isConnected()) { + disconnect(true); + } + this.connection = connection; + this.canceled = false; + return connect(); + } + + @Override + public boolean stopConnecting() { + canceled = true; + return true; + } + + @Override + public boolean connect() { + sessionState = SessionState.CONNECTING; + try { + System.setProperty("http.nonProxyHosts", "code.google.com"); + System.setProperty("socksNonProxyHosts", "code.google.com"); + + // clear previous values + System.clearProperty("socksProxyHost"); + System.clearProperty("socksProxyPort"); + System.clearProperty("http.proxyHost"); + System.clearProperty("http.proxyPort"); + + switch (connection.getProxyType()) { + case SOCKS: + System.setProperty("socksProxyHost", connection.getProxyHost()); + System.setProperty("socksProxyPort", Integer.toString(connection.getProxyPort())); + break; + case HTTP: + System.setProperty("http.proxyHost", connection.getProxyHost()); + System.setProperty("http.proxyPort", Integer.toString(connection.getProxyPort())); + Authenticator.setDefault(new MageAuthenticator(connection.getProxyUsername(), connection.getProxyPassword())); + break; + } + InvokerLocator clientLocator = new InvokerLocator(connection.getURI()); + + Map metadata = new HashMap<>(); + /* + 5.8.3.1.1. Write timeouts + The socket timeout facility offered by the JDK applies only to read operations on the socket. As of release 2.5.2, + the socket and bisocket (and also sslsocket and sslbisocket) transports offer a write timeout facility. When a client + or server is configured, in any of the usual ways, with the parameter org.jboss.remoting.transport.socket.SocketWrapper.WRITE_TIMEOUT + (actual value "writeTimeout") set to a positive value (in milliseconds), all write operations will time out if they do + not complete within the configured period. When a write operation times out, the socket upon which the write was invoked + will be closed, which is likely to result in a java.net.SocketException. + Note. A SocketException is considered to be a "retriable" exception, so, if the parameter "numberOfCallRetries" is set + to a value greater than 1, an invocation interrupted by a write timeout can be retried. + Note. The write timeout facility applies to writing of both invocations and responses. It applies to push callbacks as well. + */ + metadata.put(SocketWrapper.WRITE_TIMEOUT, "2000"); + metadata.put("generalizeSocketException", "true"); + server = (MageServer) TransporterClient.createTransporterClient(clientLocator.getLocatorURI(), MageServer.class, metadata); + + // http://docs.jboss.org/jbossremoting/docs/guide/2.5/html_single/#d0e1057 + Map clientMetadata = new HashMap<>(); + + clientMetadata.put(SocketWrapper.WRITE_TIMEOUT, "2000"); + /* generalizeSocketException + * If set to false, a failed invocation will be retried in the case of + * SocketExceptions. If set to true, a failed invocation will be retried in the case of + * SocketExceptions and also any IOException + * whose message matches the regular expression + * ^.*(?:connection.*reset|connection.*closed|broken.*pipe).*$. + * See also the "numberOfCallRetries" parameter, above. The default value is false.*/ + clientMetadata.put("generalizeSocketException", "true"); + + /* A remoting server also has the capability to detect when a client is no longer available. + * This is done by estabilishing a lease with the remoting clients that connect to a server. + * On the client side, an org.jboss.remoting.LeasePinger periodically sends PING messages to + * the server, and on the server side an org.jboss.remoting.Lease informs registered listeners + * if the PING doesn't arrive withing the specified timeout period. */ + clientMetadata.put(Client.ENABLE_LEASE, "true"); + /* + When the socket client invoker makes its first invocation, it will check to see if there is an available + socket connection in its pool. Since is the first invocation, there will not be and will create a new socket + connection and use it for making the invocation. Then when finished making invocation, will return the still + active socket connection to the pool. As more client invocations are made, is possible for the number of + socket connections to reach the maximum allowed (which is controlled by 'clientMaxPoolSize' property). At this + point, when the next client invocation is made, it will wait up to some configured number of milliseconds, at + which point it will throw an org.jboss.remoting.CannotConnectException. The number of milliseconds is given by + the parameter MicroSocketClientInvoker.CONNECTION_WAIT (actual value "connectionWait"), with a default of + 30000 milliseconds. Note that if more than one call retry is configured (see next paragraph), + the CannotConnectException will be swallowed. + Once the socket client invoker get an available socket connection from the pool, are not out of the woods yet. + For example, a network problem could cause a java.net.SocketException. There is also a possibility that the socket + connection, while still appearing to be valid, has "gone stale" while sitting in the pool. For example, a ServerThread + on the other side of the connection could time out and close its socket. If the attempt to complete an invocation + fails, then MicroSocketClientInvoker will make a number of attempts, according to the parameter "numberOfCallRetries", + with a default value of 3. Once the configured number of retries has been exhausted, + an org.jboss.remoting.InvocationFailureException will be thrown. + */ + clientMetadata.put("numberOfCallRetries", "1"); + + + /** + * I'll explain the meaning of "secondaryBindPort" and "secondaryConnectPort", and maybe that will help. + * The Remoting bisocket transport creates two ServerSockets on the server. The "primary" ServerSocket is used to create + * connections used for ordinary invocations, e.g., a request to create a JMS consumer, and the "secondary" ServerSocket + * is used to create "control" connections for internal Remoting messages. The port for the primary ServerSocket is configured + * by the "serverBindPort" parameter, and the port for the secondary ServerSocket is, by default, chosen randomly. + * The "secondaryBindPort" parameter can be used to assign a specific port to the secondary ServerSocket. Now, if there is a + * translating firewall between the client and server, the client should be given the value of the port that is translated + * to the actual binding port of the secondary ServerSocket. + * For example, your configuration will tell the secondary ServerSocket to bind to port 14000, and it will tell the client to + * connect to port 14001. It assumes that there is a firewall which will translate 14001 to 14000. Apparently, that's not happening. + */ + // secondaryBindPort - the port to which the secondary server socket is to be bound. By default, an arbitrary port is selected. + + // secondaryConnectPort - the port clients are to use to connect to the secondary server socket. + // By default, the value of secondaryBindPort is used. secondaryConnectPort is useful if the server is behind a translating firewall. + + // Indicated the max number of threads used within oneway thread pool. + clientMetadata.put(Client.MAX_NUM_ONEWAY_THREADS, "10"); + clientMetadata.put(Remoting.USE_CLIENT_CONNECTION_IDENTITY, "true"); + callbackClient = new Client(clientLocator, "callback", clientMetadata); + + Map listenerMetadata = new HashMap<>(); + if (debugMode) { + // prevent client from disconnecting while debugging + listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_PERIOD, "1000000"); + listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_TIMEOUT, "900000"); + } else { + listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_PERIOD, "15000"); + listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_TIMEOUT, "13000"); + } + callbackClient.connect(new ClientConnectionListener(), listenerMetadata); + + Map callbackMetadata = new HashMap<>(); + callbackMetadata.put(Bisocket.IS_CALLBACK_SERVER, "true"); + if (callbackHandler == null) { + callbackHandler = new CallbackHandler(); + } + callbackClient.addListener(callbackHandler, callbackMetadata); + + Set callbackConnectors = callbackClient.getCallbackConnectors(callbackHandler); + if (callbackConnectors.size() != 1) { + logger.warn("There should be one callback Connector (number existing = " + callbackConnectors.size() + ")"); + } + + logger.info("Trying to connect as " + (this.getUserName() == null ? "":this.getUserName()) + " to XMAGE server at " + connection.getHost() + ":" + connection.getPort()); + callbackClient.invoke(null); + + this.sessionId = callbackClient.getSessionId(); + boolean registerResult; + if (connection.getPassword() == null) { + UserDataView userDataView = new UserDataView(connection.getAvatarId(), + connection.isShowAbilityPickerForced(), + connection.allowRequestShowHandCards(), + connection.confirmEmptyManaPool(), + connection.getUserSkipPrioritySteps(), + connection.getFlagName()); + // for backward compatibility. don't remove twice call - first one does nothing but for version checking + registerResult = server.registerClient(connection.getUsername(), sessionId, client.getVersion()); + if (registerResult) { + server.setUserData(connection.getUsername(), sessionId, userDataView); + } + } else { + registerResult = server.registerAdmin(connection.getPassword(), sessionId, client.getVersion()); + } + if (registerResult) { + sessionState = SessionState.CONNECTED; + serverState = server.getServerState(); + if (!connection.getUsername().equals("Admin")) { + updateDatabase(connection.isForceDBComparison(), serverState); + } + logger.info("Connected as " + (this.getUserName() == null ? "":this.getUserName()) + " to MAGE server at " + connection.getHost() + ":" + connection.getPort()); + client.connected(this.getUserName() == null ? "":this.getUserName() +"@" + connection.getHost() + ":" + connection.getPort() +" "); + return true; + } + disconnect(false); + // client.showMessage("Unable to connect to server."); + } catch (MalformedURLException ex) { + logger.fatal("", ex); + client.showMessage("Unable to connect to server. " + ex.getMessage()); + } catch (UndeclaredThrowableException ex) { + String addMessage = ""; + if (ex.getCause() instanceof InvocationFailureException) { + InvocationFailureException exep = (InvocationFailureException) ex.getCause(); + if (exep.getCause() instanceof IOException) { + if (exep.getCause().getMessage().startsWith("Field hash null is not available on current")) { + addMessage = "Probabaly the server version is not compatible to the client. "; + } + } + } + if (addMessage.isEmpty()) { + logger.fatal("", ex); + } + client.showMessage("Unable to connect to server. " + addMessage + (ex.getMessage() != null ? ex.getMessage():"")); + } catch (IOException ex) { + logger.fatal("", ex); + String addMessage = ""; + if (ex.getMessage() != null && ex.getMessage().startsWith("Unable to perform invocation")) { + addMessage = "Maybe the server version is not compatible. "; + } + client.showMessage("Unable to connect to server. " + addMessage + ex.getMessage() != null ? ex.getMessage():""); + } catch (MageVersionException ex) { + if (!canceled) { + client.showMessage("Unable to connect to server. " + ex.getMessage()); + } + disconnect(false); + } catch (CannotConnectException ex) { + if (!canceled) { + handleCannotConnectException(ex); + } + } catch (Throwable t) { + logger.fatal("Unable to connect to server - ", t); + if (!canceled) { + disconnect(false); + StringBuilder sb = new StringBuilder(); + sb.append("Unable to connect to server.\n"); + for (StackTraceElement element :t.getStackTrace()) { + sb.append(element.toString()).append("\n"); + } + client.showMessage(sb.toString()); + } + } + return false; + } + + private void updateDatabase(boolean forceDBComparison, ServerState serverState) { + long cardDBVersion = CardRepository.instance.getContentVersionFromDB(); + if (forceDBComparison || serverState.getCardsContentVersion() > cardDBVersion) { + List classNames = CardRepository.instance.getClassNames(); + List cards = server.getMissingCardsData(classNames); + CardRepository.instance.addCards(cards); + CardRepository.instance.setContentVersion(serverState.getCardsContentVersion()); + logger.info("Updating client cards DB - existing cards: " + classNames.size() + " new cards: " + cards.size() + + " content versions - server: " + serverState.getCardsContentVersion() + " client: " + cardDBVersion); + } + + long expansionDBVersion = ExpansionRepository.instance.getContentVersionFromDB(); + if (forceDBComparison || serverState.getExpansionsContentVersion() > expansionDBVersion) { + List setCodes = ExpansionRepository.instance.getSetCodes(); + List expansions = server.getMissingExpansionData(setCodes); + for (ExpansionInfo expansion : expansions) { + ExpansionRepository.instance.add(expansion); + } + ExpansionRepository.instance.setContentVersion(serverState.getExpansionsContentVersion()); + logger.info("Updating client expansions DB - existing sets: " + setCodes.size() + " new sets: " + expansions.size()+ + " content versions - server: " + serverState.getExpansionsContentVersion() + " client: " + expansionDBVersion); + } + } + + private void handleCannotConnectException(CannotConnectException ex) { + logger.warn("Cannot connect", ex); + Throwable t = ex.getCause(); + String message = ""; + while (t != null) { + if (t instanceof ConnectException) { + message = "Server is likely offline." + message; + break; + } + if (t instanceof SocketException) { + message = "Check your internet connection." + message; + break; + } + if (t instanceof SocketTimeoutException) { + message = "Server is not responding." + message; + break; + } + if (t.getCause() != null && logger.isDebugEnabled()) { + message = "\n" + t.getCause().getMessage() + message; + logger.debug(t.getCause().getMessage()); + } + + t = t.getCause(); + } + client.showMessage("Unable to connect to server. " + message); + if (logger.isTraceEnabled()) { + logger.trace("StackTrace", t); + } + } + + /** + * + * @param askForReconnect - true = connection was lost because of error and ask the user if he want to try to reconnect + */ + @Override + public synchronized void disconnect(boolean askForReconnect) { + if (isConnected()) { + logger.info("DISCONNECT (still connected)"); + sessionState = SessionState.DISCONNECTING; + } + if (connection == null || sessionState == SessionState.DISCONNECTED) { + return; + } + + try { + callbackClient.removeListener(callbackHandler); + callbackClient.disconnect(); + TransporterClient.destroyTransporterClient(server); + } catch (Throwable ex) { + logger.fatal("Error disconnecting ...", ex); + } + + if (sessionState == SessionState.DISCONNECTING || sessionState == SessionState.CONNECTING) { + sessionState = SessionState.DISCONNECTED; + logger.info("Disconnected ... "); + if (askForReconnect) { + client.showError("Network error. You have been disconnected"); + } + client.disconnected(askForReconnect); // MageFrame with check to reconnect + pingTime.clear(); + } + } + + @Override + public synchronized void reconnect(Throwable throwable) { + logger.info("RECONNECT - Connected: " + isConnected()); + client.disconnected(true); + } + + @Override + public synchronized boolean sendFeedback(String title, String type, String message, String email) { + if (isConnected()) { + try { + server.sendFeedbackMessage(sessionId, connection.getUsername(), title, type, message, email); + return true; + } catch (MageException e) { + logger.error(e); + } + } + return false; + } + + class CallbackHandler implements InvokerCallbackHandler { + @Override + public void handleCallback(Callback callback) throws HandleCallbackException { + //logger.info("callback handler"); + client.processCallback((ClientCallback)callback.getCallbackObject()); + } + } + + class ClientConnectionListener implements ConnectionListener { + // http://docs.jboss.org/jbossremoting/2.5.3.SP1/html/chapter-connection-failure.html + + @Override + public void handleConnectionException(Throwable throwable, Client client) { + logger.info("connection to server lost - " + throwable.getMessage()); + throwable.printStackTrace(); + + reconnect(throwable); + } + } + + @Override + public boolean isConnected() { + if (callbackClient == null) { + return false; + } + return callbackClient.isConnected(); + } + + @Override + public String[] getPlayerTypes() { + return serverState.getPlayerTypes(); + } + + @Override + public List getGameTypes() { + return serverState.getGameTypes(); + } + + @Override + public List getTournamentGameTypes() { + return serverState.getTournamentGameTypes(); + } + + @Override + public String[] getDeckTypes() { + return serverState.getDeckTypes(); + } + + @Override + public String[] getDraftCubes() { + return serverState.getDraftCubes(); + } + + + @Override + public List getTournamentTypes() { + return serverState.getTournamentTypes(); + } + + @Override + public boolean isTestMode() { + if (serverState != null) { + return serverState.isTestMode(); + } + return false; + } + + @Override + public UUID getMainRoomId() { + try { + if (isConnected()) { + return server.getMainRoomId(); + } + } catch (MageException ex) { + handleMageException(ex); + } + return null; + } + + @Override + public UUID getRoomChatId(UUID roomId) { + try { + if (isConnected()) { + return server.getRoomChatId(roomId); + } + } catch (MageException ex) { + handleMageException(ex); + } + return null; + } + + @Override + public UUID getTableChatId(UUID tableId) { + try { + if (isConnected()) { + return server.getTableChatId(tableId); + } + } catch (MageException ex) { + handleMageException(ex); + } + return null; + } + + @Override + public UUID getGameChatId(UUID gameId) { + try { + if (isConnected()) { + return server.getGameChatId(gameId); + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return null; + } + + @Override + public TableView getTable(UUID roomId, UUID tableId) { + try { + if (isConnected()) { + return server.getTable(roomId, tableId); + } + } catch (MageException ex) { + handleMageException(ex); + } + return null; + } + + @Override + public boolean watchTable(UUID roomId, UUID tableId) { + try { + if (isConnected()) { + server.watchTable(sessionId, roomId, tableId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean watchTournamentTable(UUID tableId) { + try { + if (isConnected()) { + server.watchTournamentTable(sessionId, tableId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean joinTable(UUID roomId, UUID tableId, String playerName, String playerType, int skill, DeckCardLists deckList, String password) { + try { + if (isConnected()) { + return server.joinTable(sessionId, roomId, tableId, playerName, playerType, skill, deckList, password); + } + } catch (InvalidDeckException iex) { + handleInvalidDeckException(iex); + } catch (GameException ex) { + handleGameException(ex); + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean joinTournamentTable(UUID roomId, UUID tableId, String playerName, String playerType, int skill, DeckCardLists deckList, String password) { + try { + if (isConnected()) { + return server.joinTournamentTable(sessionId, roomId, tableId, playerName, playerType, skill, deckList, password); + } + } catch (GameException ex) { + handleGameException(ex); + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public Collection getTables(UUID roomId) throws MageRemoteException { + try { + if (isConnected()) { + return server.getTables(roomId); + } + } catch (MageException ex) { + handleMageException(ex); + throw new MageRemoteException(); + } catch (Throwable t) { + handleThrowable(t); + } + return null; + } + + @Override + public Collection getFinishedMatches(UUID roomId) throws MageRemoteException { + try { + if (isConnected()) { + return server.getFinishedMatches(roomId); + } + } catch (MageException ex) { + handleMageException(ex); + throw new MageRemoteException(); + } catch (Throwable t) { + handleThrowable(t); + } + return null; + } + + @Override + public Collection getRoomUsers(UUID roomId) throws MageRemoteException { + try { + if (isConnected()) { + return server.getRoomUsers(roomId); + } + } catch (MageException ex) { + handleMageException(ex); + throw new MageRemoteException(); + } catch (Throwable t) { + handleThrowable(t); + } + return null; + } + + @Override + public TournamentView getTournament(UUID tournamentId) throws MageRemoteException { + try { + if (isConnected()) { + return server.getTournament(tournamentId); + } + } catch (MageException ex) { + handleMageException(ex); + throw new MageRemoteException(); + } catch (Throwable t) { + handleThrowable(t); + } + return null; + } + + @Override + public UUID getTournamentChatId(UUID tournamentId) { + try { + if (isConnected()) { + return server.getTournamentChatId(tournamentId); + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return null; + } + + @Override + public boolean sendPlayerUUID(UUID gameId, UUID data) { + try { + if (isConnected()) { + server.sendPlayerUUID(gameId, sessionId, data); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean sendPlayerBoolean(UUID gameId, boolean data) { + try { + if (isConnected()) { + server.sendPlayerBoolean(gameId, sessionId, data); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean sendPlayerInteger(UUID gameId, int data) { + try { + if (isConnected()) { + server.sendPlayerInteger(gameId, sessionId, data); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean sendPlayerString(UUID gameId, String data) { + try { + if (isConnected()) { + server.sendPlayerString(gameId, sessionId, data); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean sendPlayerManaType(UUID gameId, UUID playerId, ManaType data) { + try { + if (isConnected()) { + server.sendPlayerManaType(gameId, playerId, sessionId, data); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public DraftPickView sendCardPick(UUID draftId, UUID cardId, Set hiddenCards) { + try { + if (isConnected()) { + return server.sendCardPick(draftId, sessionId, cardId, hiddenCards); + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return null; + } + + @Override + public DraftPickView sendCardMark(UUID draftId, UUID cardId) { + try { + if (isConnected()) { + server.sendCardMark(draftId, sessionId, cardId); + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return null; + } + + @Override + public boolean joinChat(UUID chatId) { + try { + if (isConnected()) { + server.joinChat(chatId, sessionId, connection.getUsername()); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean leaveChat(UUID chatId) { +// lock.readLock().lock(); + try { + if (isConnected()) { + server.leaveChat(chatId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); +// } finally { +// lock.readLock().unlock(); + } + return false; + } + + @Override + public boolean sendChatMessage(UUID chatId, String message) { +// lock.readLock().lock(); + try { + if (isConnected()) { + server.sendChatMessage(chatId, connection.getUsername(), message); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); +// } finally { +// lock.readLock().unlock(); + } + return false; + } + + @Override + public boolean sendBroadcastMessage(String message) { + try { + if (isConnected()) { + server.sendBroadcastMessage(sessionId, message); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean joinGame(UUID gameId) { + try { + if (isConnected()) { + server.joinGame(gameId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean joinDraft(UUID draftId) { + try { + if (isConnected()) { + server.joinDraft(draftId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean joinTournament(UUID tournamentId) { + try { + if (isConnected()) { + server.joinTournament(tournamentId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean watchGame(UUID gameId) { + try { + if (isConnected()) { + server.watchGame(gameId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean replayGame(UUID gameId) { + try { + if (isConnected()) { + server.replayGame(gameId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public TableView createTable(UUID roomId, MatchOptions matchOptions) { + try { + if (isConnected()) { + return server.createTable(sessionId, roomId, matchOptions); + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return null; + } + + @Override + public TableView createTournamentTable(UUID roomId, TournamentOptions tournamentOptions) { + try { + if (isConnected()) { + return server.createTournamentTable(sessionId, roomId, tournamentOptions); + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return null; + } + + @Override + public boolean isTableOwner(UUID roomId, UUID tableId) { + try { + if (isConnected()) { + return server.isTableOwner(sessionId, roomId, tableId); + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean removeTable(UUID roomId, UUID tableId) { + try { + if (isConnected()) { + server.removeTable(sessionId, roomId, tableId); + + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + /** + * Remove table - called from admin console + * @param tableId + * @return + */ + @Override + public boolean removeTable(UUID tableId) { + try { + if (isConnected()) { + server.removeTable(sessionId, tableId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean swapSeats(UUID roomId, UUID tableId, int seatNum1, int seatNum2) { + try { + if (isConnected()) { + server.swapSeats(sessionId, roomId, tableId, seatNum1, seatNum2); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean leaveTable(UUID roomId, UUID tableId) { + try { + if (isConnected() && server.leaveTable(sessionId, roomId, tableId)) { + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean startMatch(UUID roomId, UUID tableId) { + try { + if (isConnected()) { + return (server.startMatch(sessionId, roomId, tableId)); + } + } catch (MageException ex) { + handleMageException(ex); + } + return false; + } + + @Override + public boolean startTournament(UUID roomId, UUID tableId) { + try { + if (isConnected() && server.startTournament(sessionId, roomId, tableId)) { + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + +// @Override +// public boolean startChallenge(UUID roomId, UUID tableId, UUID challengeId) { +>>>>>>> master // try { // if (isConnected()) { // server.endUserSession(sessionId, userSessionId); @@ -1364,6 +2470,7 @@ // } // return false; // } +<<<<<<< HEAD // // private void handleThrowable(Throwable t) { // logger.fatal("Communication error", t); @@ -1466,3 +2573,363 @@ // return new PasswordAuthentication (username, password.toCharArray()); // } //} +======= + + @Override + public boolean submitDeck(UUID tableId, DeckCardLists deck) { + try { + if (isConnected()) { + return server.submitDeck(sessionId, tableId, deck); + } + } catch (GameException ex) { + handleGameException(ex); + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean updateDeck(UUID tableId, DeckCardLists deck) { + try { + if (isConnected()) { + server.updateDeck(sessionId, tableId, deck); + return true; + } + } catch (GameException ex) { + handleGameException(ex); + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean quitMatch(UUID gameId) { + try { + if (isConnected()) { + server.quitMatch(gameId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean quitTournament(UUID tournamentId) { + try { + if (isConnected()) { + server.quitTournament(tournamentId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean quitDraft(UUID draftId) { + try { + if (isConnected()) { + server.quitDraft(draftId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } + return false; + } + + @Override + public boolean sendPlayerAction(PlayerAction passPriorityAction, UUID gameId, Object data) { + try { + if (isConnected()) { + server.sendPlayerAction(passPriorityAction, gameId, sessionId, data); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean stopWatching(UUID gameId) { + try { + if (isConnected()) { + server.stopWatching(gameId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean startReplay(UUID gameId) { + try { + if (isConnected()) { + server.startReplay(gameId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean stopReplay(UUID gameId) { + try { + if (isConnected()) { + server.stopReplay(gameId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean nextPlay(UUID gameId) { + try { + if (isConnected()) { + server.nextPlay(gameId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean previousPlay(UUID gameId) { + try { + if (isConnected()) { + server.previousPlay(gameId, sessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean skipForward(UUID gameId, int moves) { + try { + if (isConnected()) { + server.skipForward(gameId, sessionId, moves); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean cheat(UUID gameId, UUID playerId, DeckCardLists deckList) { + try { + if (isConnected()) { + server.cheat(gameId, sessionId, playerId, deckList); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public List getUsers() { + try { + if (isConnected()) { + return server.getUsers(sessionId); + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return null; + } + + @Override + public List getServerMessages() { + try { + if (isConnected()) { + return (List) CompressUtil.decompress(server.getServerMessagesCompressed(sessionId)); + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return null; + } + + @Override + public boolean disconnectUser(String userSessionId) { + try { + if (isConnected()) { + server.disconnectUser(sessionId, userSessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean endUserSession(String userSessionId) { + try { + if (isConnected()) { + server.endUserSession(sessionId, userSessionId); + return true; + } + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + private void handleThrowable(Throwable t) { + logger.fatal("Communication error", t); + // Probably this can cause hanging the client under certain circumstances as the disconnect method is synchronized + // so check if it's needed + // disconnect(true); + } + + private void handleMageException(MageException ex) { + logger.fatal("Server error", ex); + client.showError(ex.getMessage()); + } + + private void handleInvalidDeckException(InvalidDeckException iex) { + logger.warn(iex.getMessage() + "\n" + iex.getInvalid()); + client.showError(iex.getMessage() + "\n" + iex.getInvalid()); + } + + private void handleGameException(GameException ex) { + logger.warn(ex.getMessage()); + client.showError(ex.getMessage()); + } + + + @Override + public String getUserName() { + return connection.getUsername(); + } + + @Override + public boolean updatePreferencesForServer(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps, String flagName) { + try { + if (isConnected()) { + UserDataView userDataView = new UserDataView(avatarId, showAbilityPickerForced, allowRequestShowHandCards, confirmEmptyManaPool, userSkipPrioritySteps, flagName); + server.setUserData(connection.getUsername(), sessionId, userDataView); + } + return true; + } catch (MageException ex) { + handleMageException(ex); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public boolean ping() { + try { + if (isConnected()) { + long startTime = System.nanoTime(); + if (!server.ping(sessionId, pingInfo)) { + logger.error("Ping failed: " + this.getUserName() + " Session: " + sessionId + " to MAGE server at " + connection.getHost() +":" + connection.getPort()); + throw new MageException("Ping failed"); + } + pingTime.add(System.nanoTime() - startTime); + long milliSeconds = TimeUnit.MILLISECONDS.convert(pingTime.getLast(), TimeUnit.NANOSECONDS); + String lastPing = milliSeconds > 0 ? milliSeconds+"ms" : "<1ms"; + if (pingTime.size() > PING_CYCLES) { + pingTime.poll(); + } + long sum = 0; + for (Long time :pingTime) { + sum += time; + } + milliSeconds = TimeUnit.MILLISECONDS.convert(sum / pingTime.size(), TimeUnit.NANOSECONDS); + pingInfo = lastPing + " (Av: " + (milliSeconds > 0 ? milliSeconds + "ms":"<1ms")+")"; + } + return true; + } catch (MageException ex) { + handleMageException(ex); + disconnect(true); + } catch (Throwable t) { + handleThrowable(t); + } + return false; + } + + @Override + public String getVersionInfo() { + if (serverState != null) { + return serverState.getVersion().toString(); + } else { + return ""; + } + } + +} + + +class MageAuthenticator extends Authenticator { + + private final String username; + private final String password; + + public MageAuthenticator(String username, String password) { + this.username = username; + this.password = password; + } + + @Override + public PasswordAuthentication getPasswordAuthentication () { + return new PasswordAuthentication (username, password.toCharArray()); + } +} +>>>>>>> master diff --git a/Mage.Common/src/mage/remote/interfaces/ClientData.java b/Mage.Common/src/mage/remote/interfaces/ClientData.java index e2cc74ff1ff..2a9fe56b384 100644 --- a/Mage.Common/src/mage/remote/interfaces/ClientData.java +++ b/Mage.Common/src/mage/remote/interfaces/ClientData.java @@ -36,5 +36,5 @@ public interface ClientData { String getUserName(); - boolean updatePreferencesForServer(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, UserSkipPrioritySteps userSkipPrioritySteps); + boolean updatePreferencesForServer(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps, String flagName); } diff --git a/Mage.Common/src/mage/remote/interfaces/GamePlay.java b/Mage.Common/src/mage/remote/interfaces/GamePlay.java index d99f17011cb..f54a193fd31 100644 --- a/Mage.Common/src/mage/remote/interfaces/GamePlay.java +++ b/Mage.Common/src/mage/remote/interfaces/GamePlay.java @@ -78,6 +78,7 @@ public interface GamePlay { * * @param passPriorityAction * @param gameId + * @param Data * @return */ boolean sendPlayerAction(PlayerAction passPriorityAction, UUID gameId, Object Data); diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 85b6b1df8cc..d0845cd7ae6 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -114,20 +114,19 @@ public class CardView extends SimpleCardView { protected boolean isPlayable; protected boolean isChoosable; protected boolean selected; - protected boolean canAttack; - protected boolean gameObject; + protected boolean canAttack; public CardView(Card card) { - this(card, null, null, false); + this(card, null, false); } public CardView(Card card, UUID cardId) { - this(card, null, null, false); + this(card, null, false); this.id = cardId; } public CardView(Card card, Game game, UUID cardId) { - this(card, game, null, false); + this(card, game, false); this.id = cardId; } @@ -135,15 +134,12 @@ public class CardView extends SimpleCardView { * * @param card * @param game - * @param cardId not used? * @param controlled is the card view created for the card controller - used for morph / face down cards to know which player may see information for the card */ - public CardView(Card card, Game game, UUID cardId, boolean controlled) { - super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode()); + public CardView(Card card, Game game, boolean controlled) { + super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), game != null); // no information available for face down cards as long it's not a controlled face down morph card - // TODO: Better handle this in Framework (but currently I'm not sure how to do it there) LevelX2 - this.gameObject = game != null; - + // TODO: Better handle this in Framework (but currently I'm not sure how to do it there) LevelX2 if (game != null && card.isFaceDown(game)) { this.fillEmpty(card, controlled); if (card instanceof Spell) { @@ -241,7 +237,7 @@ public class CardView extends SimpleCardView { this.cardTypes = card.getCardType(); this.subTypes = card.getSubtype(); this.superTypes = card.getSupertype(); - this.color = card.getColor(); + this.color = card.getColor(game); this.canTransform = card.canTransform(); this.flipCard = card.isFlipCard(); this.faceDown = game != null ? card.isFaceDown(game) : false; @@ -301,7 +297,7 @@ public class CardView extends SimpleCardView { } public CardView(MageObject object) { - super(object.getId(), "", 0, false, ""); + super(object.getId(), "", 0, false, "", true); this.name = object.getName(); this.displayName = object.getName(); if (object instanceof Permanent) { @@ -317,7 +313,7 @@ public class CardView extends SimpleCardView { this.cardTypes = object.getCardType(); this.subTypes = object.getSubtype(); this.superTypes = object.getSupertype(); - this.color = object.getColor(); + this.color = object.getColor(null); this.manaCost = object.getManaCost().getSymbols(); this.convertedManaCost = object.getManaCost().convertedManaCost(); if (object instanceof PermanentToken) { @@ -345,11 +341,12 @@ public class CardView extends SimpleCardView { } protected CardView() { - super(null, "", 0, false, ""); + super(null, "", 0, false, "", true); } public CardView(EmblemView emblem) { this(true); + this.gameObject = true; this.id = emblem.getId(); this.mageObjectType = MageObjectType.EMBLEM; this.name = emblem.getName(); @@ -368,13 +365,6 @@ public class CardView extends SimpleCardView { fillEmpty(null, false); } - - public CardView(String name) { - this(true); - this.name = name; - this.displayName = name; - } - private void fillEmpty(Card card, boolean controlled) { this.name = "Face Down"; this.displayName = name; @@ -431,7 +421,7 @@ public class CardView extends SimpleCardView { this.cardTypes = token.getCardType(); this.subTypes = token.getSubtype(); this.superTypes = token.getSupertype(); - this.color = token.getColor(); + this.color = token.getColor(null); this.manaCost = token.getManaCost().getSymbols(); this.rarity = Rarity.NA; this.type = token.getTokenType(); @@ -736,8 +726,5 @@ public class CardView extends SimpleCardView { public void setCanAttack(boolean canAttack) { this.canAttack = canAttack; } - - public boolean isGameObject() { - return gameObject; - } + } diff --git a/Mage.Common/src/mage/view/CardsView.java b/Mage.Common/src/mage/view/CardsView.java index 57284013352..a39a9d6e007 100644 --- a/Mage.Common/src/mage/view/CardsView.java +++ b/Mage.Common/src/mage/view/CardsView.java @@ -66,7 +66,7 @@ public class CardsView extends LinkedHashMap { public CardsView(Game game, Collection cards) { for (Card card: cards) { - this.put(card.getId(), new CardView(card, game, null, false)); + this.put(card.getId(), new CardView(card, game, false)); } } diff --git a/Mage.Common/src/mage/view/CommanderView.java b/Mage.Common/src/mage/view/CommanderView.java index 46e012b374c..843a38ae2ce 100644 --- a/Mage.Common/src/mage/view/CommanderView.java +++ b/Mage.Common/src/mage/view/CommanderView.java @@ -40,7 +40,7 @@ import mage.game.command.Commander; public class CommanderView extends CardView implements CommandObjectView, Serializable{ public CommanderView(Commander commander, Card sourceCard, Game game) { - super(sourceCard, game, null, false); + super(sourceCard, game, false); this.mageObjectType = MageObjectType.COMMANDER; } } diff --git a/Mage.Common/src/mage/view/DeckView.java b/Mage.Common/src/mage/view/DeckView.java index dccd0f46b0f..1542ac4a80e 100644 --- a/Mage.Common/src/mage/view/DeckView.java +++ b/Mage.Common/src/mage/view/DeckView.java @@ -41,8 +41,8 @@ public class DeckView implements Serializable { public DeckView(Deck deck) { name = deck.getName(); - cards = new SimpleCardsView(deck.getCards()); - sideboard = new SimpleCardsView(deck.getSideboard()); + cards = new SimpleCardsView(deck.getCards(), false); + sideboard = new SimpleCardsView(deck.getSideboard(), false); } public String getName() { diff --git a/Mage.Common/src/mage/view/DraftPickView.java b/Mage.Common/src/mage/view/DraftPickView.java index 82ee5cc0996..b1bf2c22b59 100644 --- a/Mage.Common/src/mage/view/DraftPickView.java +++ b/Mage.Common/src/mage/view/DraftPickView.java @@ -44,8 +44,8 @@ public class DraftPickView implements Serializable { protected int timeout; public DraftPickView(DraftPlayer player, int timeout) { - this.booster = new SimpleCardsView(player.getBooster()); - this.picks = new SimpleCardsView(player.getDeck().getSideboard()); + this.booster = new SimpleCardsView(player.getBooster(), false); + this.picks = new SimpleCardsView(player.getDeck().getSideboard(), false); this.picking = player.isPicking(); this.timeout = timeout; } diff --git a/Mage.Common/src/mage/view/ExileView.java b/Mage.Common/src/mage/view/ExileView.java index b6c3ecc87bf..bd0c03752a3 100644 --- a/Mage.Common/src/mage/view/ExileView.java +++ b/Mage.Common/src/mage/view/ExileView.java @@ -47,7 +47,7 @@ public class ExileView extends CardsView { this.name = exileZone.getName(); this.id = exileZone.getId(); for (Card card: exileZone.getCards(game)) { - this.put(card.getId(), new CardView(card, game, card.getId(), false)); + this.put(card.getId(), new CardView(card, game, false)); } } diff --git a/Mage.Common/src/mage/view/GameView.java b/Mage.Common/src/mage/view/GameView.java index 467780abf1c..b75d8045131 100644 --- a/Mage.Common/src/mage/view/GameView.java +++ b/Mage.Common/src/mage/view/GameView.java @@ -81,6 +81,7 @@ public class GameView implements Serializable { private boolean special = false; private final boolean isPlayer; private final int spellsCastCurrentTurn; + private final boolean rollbackTurnsAllowed; public GameView(GameState state, Game game, UUID createdForPlayerId, UUID watcherUserId) { @@ -136,7 +137,7 @@ public class GameView implements Serializable { } else { // Spell - stack.put(stackObject.getId(), new CardView((Spell)stackObject, game, null, stackObject.getControllerId().equals(createdForPlayerId))); + stack.put(stackObject.getId(), new CardView((Spell)stackObject, game, stackObject.getControllerId().equals(createdForPlayerId))); checkPaid(stackObject.getId(), (Spell)stackObject); } //stackOrder.add(stackObject.getId()); @@ -179,7 +180,8 @@ public class GameView implements Serializable { spellsCastCurrentTurn = watcher.getAmountOfSpellsAllPlayersCastOnCurrentTurn(); } else { spellsCastCurrentTurn = 0; - } + } + rollbackTurnsAllowed = game.getOptions().rollbackTurnsAllowed; } private void checkPaid(UUID uuid, StackAbility stackAbility) { @@ -322,5 +324,9 @@ public class GameView implements Serializable { public int getSpellsCastCurrentTurn() { return spellsCastCurrentTurn; } + + public boolean isRollbackTurnsAllowed() { + return rollbackTurnsAllowed; + } } diff --git a/Mage.Common/src/mage/view/LookedAtView.java b/Mage.Common/src/mage/view/LookedAtView.java index a7485e7120f..db1759c0c08 100644 --- a/Mage.Common/src/mage/view/LookedAtView.java +++ b/Mage.Common/src/mage/view/LookedAtView.java @@ -46,7 +46,7 @@ public class LookedAtView implements Serializable { public LookedAtView(String name, Cards cards, Game game) { this.name = name; for (Card card: cards.getCards(game)) { - this.cards.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode())); + this.cards.put(card.getId(), new CardView(card, game, card.getId())); } } diff --git a/Mage.Common/src/mage/view/PermanentView.java b/Mage.Common/src/mage/view/PermanentView.java index 4ce0246ea05..2b7fe476f00 100644 --- a/Mage.Common/src/mage/view/PermanentView.java +++ b/Mage.Common/src/mage/view/PermanentView.java @@ -62,7 +62,7 @@ public class PermanentView extends CardView { private final boolean attachedToPermanent; public PermanentView(Permanent permanent, Card card, UUID createdForPlayerId, Game game) { - super(permanent, game, null, permanent.getControllerId().equals(createdForPlayerId)); + super(permanent, game, permanent.getControllerId().equals(createdForPlayerId)); this.controlled = permanent.getControllerId().equals(createdForPlayerId); this.rules = permanent.getRules(game); this.tapped = permanent.isTapped(); diff --git a/Mage.Common/src/mage/view/PlayerView.java b/Mage.Common/src/mage/view/PlayerView.java index 343e30332c7..3e9eb437d81 100644 --- a/Mage.Common/src/mage/view/PlayerView.java +++ b/Mage.Common/src/mage/view/PlayerView.java @@ -95,12 +95,12 @@ public class PlayerView implements Serializable { this.hasLeft = player.hasLeft(); for (Card card: player.getGraveyard().getCards(game)) { - graveyard.put(card.getId(), new CardView(card, game, card.getId(), false)); + graveyard.put(card.getId(), new CardView(card, game, false)); } for (ExileZone exileZone : game.getExile().getExileZones()) { for (Card card : exileZone.getCards(game)) { if (player.getId().equals(card.getOwnerId())) { - exile.put(card.getId(), new CardView(card, game, card.getId(), false)); // unnown if it's allowed to look under a face down card + exile.put(card.getId(), new CardView(card, game, false)); // unnown if it's allowed to look under a face down card } } } @@ -115,7 +115,7 @@ public class PlayerView implements Serializable { if (player.getUserData() != null) { this.userDataView = new UserDataView(player.getUserData()); } else { - this.userDataView = new UserDataView(0, false, false, null); + this.userDataView = new UserDataView(0, false, false, true, null,"world.png"); } for (CommandObject commandObject : game.getState().getCommand()) { diff --git a/Mage.Common/src/mage/view/RevealedView.java b/Mage.Common/src/mage/view/RevealedView.java index 3f067267cda..d5f086e4101 100644 --- a/Mage.Common/src/mage/view/RevealedView.java +++ b/Mage.Common/src/mage/view/RevealedView.java @@ -40,12 +40,12 @@ import mage.game.Game; public class RevealedView implements Serializable { private final String name; - private final SimpleCardsView cards = new SimpleCardsView(); + private final CardsView cards = new CardsView(); public RevealedView(String name, Cards cards, Game game) { this.name = name; for (Card card: cards.getCards(game)) { - this.cards.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode())); + this.cards.put(card.getId(), new CardView(card, game, card.getId())); } } @@ -53,7 +53,7 @@ public class RevealedView implements Serializable { return name; } - public SimpleCardsView getCards() { + public CardsView getCards() { return cards; } } diff --git a/Mage.Common/src/mage/view/SimpleCardView.java b/Mage.Common/src/mage/view/SimpleCardView.java index daf216d72cc..fd8abe538c0 100644 --- a/Mage.Common/src/mage/view/SimpleCardView.java +++ b/Mage.Common/src/mage/view/SimpleCardView.java @@ -41,13 +41,18 @@ public class SimpleCardView implements Serializable { protected String tokenSetCode; protected int cardNumber; protected boolean usesVariousArt; + protected boolean gameObject; public SimpleCardView(UUID id, String expansionSetCode, int cardNumber, boolean usesVariousArt, String tokenSetCode) { + this(id, expansionSetCode, cardNumber, usesVariousArt, tokenSetCode, false); + } + public SimpleCardView(UUID id, String expansionSetCode, int cardNumber, boolean usesVariousArt, String tokenSetCode, boolean isGameObject) { this.id = id; this.expansionSetCode = expansionSetCode; this.cardNumber = cardNumber; this.usesVariousArt = usesVariousArt; this.tokenSetCode = tokenSetCode; + this.gameObject = isGameObject; } public UUID getId() { @@ -69,5 +74,8 @@ public class SimpleCardView implements Serializable { public String getTokenSetCode() { return tokenSetCode; } - + + public boolean isGameObject() { + return gameObject; + } } diff --git a/Mage.Common/src/mage/view/SimpleCardsView.java b/Mage.Common/src/mage/view/SimpleCardsView.java index d3763e4eedd..909d0ee2866 100644 --- a/Mage.Common/src/mage/view/SimpleCardsView.java +++ b/Mage.Common/src/mage/view/SimpleCardsView.java @@ -42,9 +42,9 @@ public class SimpleCardsView extends LinkedHashMap { public SimpleCardsView() {} - public SimpleCardsView(Collection cards) { + public SimpleCardsView(Collection cards, boolean isGameObject) { for (Card card: cards) { - this.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode())); + this.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), isGameObject)); } } diff --git a/Mage.Common/src/mage/view/StackAbilityView.java b/Mage.Common/src/mage/view/StackAbilityView.java index b8a08b07692..8cb82fa2ffe 100644 --- a/Mage.Common/src/mage/view/StackAbilityView.java +++ b/Mage.Common/src/mage/view/StackAbilityView.java @@ -64,12 +64,12 @@ public class StackAbilityView extends CardView { this.cardTypes = ability.getCardType(); this.subTypes = ability.getSubtype(); this.superTypes = ability.getSupertype(); - this.color = ability.getColor(); + this.color = ability.getColor(game); this.manaCost = ability.getManaCost().getSymbols(); this.cardTypes = ability.getCardType(); this.subTypes = ability.getSubtype(); this.superTypes = ability.getSupertype(); - this.color = ability.getColor(); + this.color = ability.getColor(game); this.manaCost = ability.getManaCost().getSymbols(); this.power = ability.getPower().toString(); this.toughness = ability.getToughness().toString(); diff --git a/Mage.Common/src/mage/view/UserDataView.java b/Mage.Common/src/mage/view/UserDataView.java index 48aed4d25c4..9bf65b6f2e7 100644 --- a/Mage.Common/src/mage/view/UserDataView.java +++ b/Mage.Common/src/mage/view/UserDataView.java @@ -15,13 +15,18 @@ public class UserDataView implements Serializable { protected int userGroup; protected boolean showAbilityPickerForced; protected boolean allowRequestShowHandCards; + protected boolean confirmEmptyManaPool; protected UserSkipPrioritySteps userSkipPrioritySteps; + String flagName; - public UserDataView(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, UserSkipPrioritySteps userSkipPrioritySteps) { + public UserDataView(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, + boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps, String flagName) { this.avatarId = avatarId; this.showAbilityPickerForced = showAbilityPickerForced; this.allowRequestShowHandCards = allowRequestShowHandCards; this.userSkipPrioritySteps = userSkipPrioritySteps; + this.confirmEmptyManaPool = confirmEmptyManaPool; + this.flagName = flagName; } public UserDataView(UserData userData) { @@ -30,6 +35,8 @@ public class UserDataView implements Serializable { this.allowRequestShowHandCards = userData.isAllowRequestShowHandCards(); this.showAbilityPickerForced = userData.isShowAbilityPickerForced(); this.userSkipPrioritySteps = userData.getUserSkipPrioritySteps(); + this.confirmEmptyManaPool = userData.confirmEmptyManaPool(); + this.flagName = userData.getFlagName(); } public int getAvatarId() { @@ -47,5 +54,13 @@ public class UserDataView implements Serializable { public UserSkipPrioritySteps getUserSkipPrioritySteps() { return userSkipPrioritySteps; } + + public boolean confirmEmptyManaPool() { + return confirmEmptyManaPool; + } + + public String getFlagName() { + return flagName; + } } diff --git a/Mage.Common/src/mage/view/UsersView.java b/Mage.Common/src/mage/view/UsersView.java index 1a7a28ec978..c5e8522bec4 100644 --- a/Mage.Common/src/mage/view/UsersView.java +++ b/Mage.Common/src/mage/view/UsersView.java @@ -37,18 +37,24 @@ public class UsersView implements Serializable { private static final long serialVersionUID = 1L; + private final String flagName; private final String userName; private final String infoState; private final String infoGames; private final String infoPing; - public UsersView(String userName, String infoState, String infoGames, String infoPing) { + public UsersView(String flagName, String userName, String infoState, String infoGames, String infoPing) { + this.flagName = flagName; this.userName = userName; this.infoState = infoState; this.infoGames = infoGames; this.infoPing = infoPing; } + public String getFlagName() { + return flagName; + } + public String getUserName() { return userName; } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java index 45e68e207a2..7144e68b5d8 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java @@ -34,6 +34,7 @@ import java.util.List; import java.util.Map; import mage.abilities.common.CanBeYourCommanderAbility; import mage.cards.Card; +import mage.cards.SplitCard; import mage.cards.decks.Deck; import mage.cards.decks.DeckValidator; import mage.constants.CardType; @@ -170,15 +171,13 @@ public class TinyLeaders extends DeckValidator { if (!bannedCommander.contains(commander.getName())) { FilterMana color = CardUtil.getColorIdentity(commander); for (Card card : deck.getCards()) { - if (!cardHasValideColor(color, card)) { - invalid.put(card.getName(), "Invalid color (" + commander.getName() + ")"); - valid = false; + if (!isCardFormatValid(card, commander, color)) { + valid = false; } - - //905.5b - Converted mana cost must be 3 or less - if (card.getManaCost().convertedManaCost() > 3) { - invalid.put(card.getName(), "Invalid cost (" + card.getManaCost().convertedManaCost() + ")"); - valid = false; + } + for (Card card : deck.getSideboard()) { + if (!isCardFormatValid(card, commander, color)) { + valid = false; } } } else { @@ -197,6 +196,31 @@ public class TinyLeaders extends DeckValidator { return valid; } + private boolean isCardFormatValid(Card card, Card commander, FilterMana color) { + if (!cardHasValideColor(color, card)) { + invalid.put(card.getName(), "Invalid color (" + commander.getName() + ")"); + return false; + } + + //905.5b - Converted mana cost must be 3 or less + if (card instanceof SplitCard) { + if (((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost() > 3) { + invalid.put(card.getName(), "Invalid cost (" + ((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost() + ")"); + return false; + } + if (((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost() > 3 ) { + invalid.put(card.getName(), "Invalid cost (" + ((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost() + ")"); + return false; + } + } else { + if (card.getManaCost().convertedManaCost() > 3) { + invalid.put(card.getName(), "Invalid cost (" + card.getManaCost().convertedManaCost() + ")"); + return false; + } + } + return true; + } + /** * * @param commander FilterMana object with Color Identity of Commander set diff --git a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/src/mage/game/CommanderFreeForAll.java b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/src/mage/game/CommanderFreeForAll.java index 1dc151f90b3..81371831294 100644 --- a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/src/mage/game/CommanderFreeForAll.java +++ b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/src/mage/game/CommanderFreeForAll.java @@ -51,9 +51,9 @@ public class CommanderFreeForAll extends GameCommanderImpl { } @Override - protected void init(UUID choosingPlayerId, GameOptions gameOptions) { + protected void init(UUID choosingPlayerId) { startingPlayerSkipsDraw = false; - super.init(choosingPlayerId, gameOptions); + super.init(choosingPlayerId); } @Override diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java index 41f2c60d263..12b68a296ea 100644 --- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java @@ -59,8 +59,8 @@ public class TwoPlayerDuel extends GameImpl { } @Override - protected void init(UUID choosingPlayerId, GameOptions gameOptions) { - super.init(choosingPlayerId, gameOptions); + protected void init(UUID choosingPlayerId) { + super.init(choosingPlayerId); state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); } 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 abb13ee7740..da899f41f40 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 @@ -63,6 +63,7 @@ import mage.target.Targets; import java.io.File; import java.util.*; import java.util.concurrent.*; +import mage.constants.AbilityType; /** * @@ -86,7 +87,8 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { protected Set actionCache; private static final List optimizers = new ArrayList<>(); protected int lastLoggedTurn = 0; - + Random random = new Random(); + protected static final String BLANKS = "..............................................."; static { optimizers.add(new LevelUpOptimizer()); optimizers.add(new EquipOptimizer()); @@ -436,8 +438,12 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { }); pool.execute(task); try { - logger.debug("maxThink: " + maxThink + " seconds"); - return task.get(maxThink, TimeUnit.SECONDS); + int maxSeconds = maxThink; + if (!ALLOW_INTERRUPT) { + maxSeconds = 3600; + } + logger.debug("maxThink: " + maxSeconds + " seconds "); + return task.get(maxSeconds, TimeUnit.SECONDS); } catch (TimeoutException e) { logger.info("simulating - timed out"); task.cancel(true); @@ -461,7 +467,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { } Game game = node.getGame(); int val; - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); val = GameStateEvaluator2.evaluate(playerId, game); logger.trace("interrupted - " + val); @@ -505,7 +511,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { } protected int simulatePriority(SimulationNode2 node, Game game, int depth, int alpha, int beta) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.info("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -522,7 +528,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { int counter = 0; for (Ability action : allActions) { counter++; - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.info("Sim Prio [" + depth + "] -- interrupted"); break; @@ -537,16 +543,24 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { } if (!sim.gameOver(null) && action.isUsesStack()) { // only pass if the last action uses the stack - sim.getPlayer(currentPlayer.getId()).pass(game); - sim.getPlayerList().getNext(); + UUID nextPlayerId = sim.getPlayerList().get(); + do { + sim.getPlayer(nextPlayerId).pass(game); + nextPlayerId = sim.getPlayerList().getNext(); + } while (nextPlayerId != this.getId()); } SimulationNode2 newNode = new SimulationNode2(node, sim, action, depth, currentPlayer.getId()); - logger.trace(new StringBuilder("Sim Prio [").append(depth).append("]#").append(counter).append(" -- newNode (").append(action.toString()).append(") ").append(newNode.hashCode()).append(" parent node ").append(node.hashCode())); - // int testVal = GameStateEvaluator2.evaluate(currentPlayer.getId(), sim); - sim.checkStateAndTriggered(); - int val = addActions(newNode, depth - 1, alpha, beta); - + int val; + if (action instanceof PassAbility) { + // Stop to simulate deeper if PassAbility + val = GameStateEvaluator2.evaluate(this.getId(), sim); +// logger.info("evaluate = " + val ); + } else { + val = addActions(newNode, depth - 1, alpha, beta); +// logger.info("addAction = " + val ); + } + logger.debug("Sim Prio " + BLANKS.substring(0, 2 + (maxDepth-depth) * 3)+ "["+depth+"]#"+counter+" <" + val +"> - ("+action.toString()+") "+newNode.hashCode()+" parent node "+node.hashCode()); if (logger.isInfoEnabled() && depth == maxDepth) { StringBuilder sb = new StringBuilder("Sim Prio [").append(depth).append("] #").append(counter) .append(" <").append(val).append("> (").append(action) @@ -564,10 +578,10 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { } if (currentPlayer.getId().equals(playerId)) { - if (action instanceof PassAbility) { - val = val -15; // passivity penalty + if (depth == maxDepth && action instanceof PassAbility) { + val = val - PASSIVITY_PENALTY; // passivity penalty } - if (val > alpha) { + if (val > alpha || (depth == maxDepth && val == alpha && random.nextBoolean())) { // Adding random for equal value to get change sometimes alpha = val; bestNode = newNode; bestNode.setScore(val); @@ -1311,7 +1325,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { private boolean checkForRepeatedAction(Game sim, SimulationNode2 node, Ability action, UUID playerId) { // pass or casting two times a spell multiple times on hand is ok - if (action instanceof PassAbility || action instanceof SpellAbility) { + if (action instanceof PassAbility || action instanceof SpellAbility || action.getAbilityType().equals(AbilityType.MANA)) { return false; } int newVal = GameStateEvaluator2.evaluate(playerId, sim); diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java index 4b443351f06..22037b2ad53 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java @@ -84,6 +84,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { public ComputerPlayer7(final ComputerPlayer7 player) { super(player); + this.allowBadMoves = player.allowBadMoves; } @Override @@ -244,7 +245,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { logger.trace("Add Action [" + depth + "] " + node.getAbilities().toString() + " a: " + alpha + " b: " + beta); } Game game = node.getGame(); - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -349,7 +350,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { protected int simulateCombat(Game game, SimulationNode2 node, int depth, int alpha, int beta, boolean counter) { Integer val = null; - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -395,7 +396,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { protected int simulateAttackers(Game game, SimulationNode2 node, UUID attackerId, int depth, int alpha, int beta, boolean counter) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -472,7 +473,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } protected int simulateBlockers(Game game, SimulationNode2 node, UUID defenderId, int depth, int alpha, int beta, boolean counter) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -556,7 +557,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } /*protected int simulateCounterAttack(Game game, SimulationNode2 node, int depth, int alpha, int beta) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUP && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -583,7 +584,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { }*/ protected void simulateStep(Game game, Step step) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return; @@ -603,7 +604,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } protected void finishCombat(Game game) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return; @@ -614,7 +615,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } protected int simulatePostCombatMain(Game game, SimulationNode2 node, int depth, int alpha, int beta) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -632,7 +633,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } protected void simulateToEnd(Game game) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return; diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java index d58a47dbaa0..40e947230c6 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java @@ -46,6 +46,7 @@ import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.Effect; import mage.cards.Card; import mage.choices.Choice; +import mage.constants.AbilityType; import mage.game.Game; import mage.game.combat.Combat; import mage.game.events.GameEvent; @@ -124,6 +125,9 @@ public class SimulatedPlayer2 extends ComputerPlayer { List playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer); playables = filterAbilities(game, playables, suggested); for (Ability ability: playables) { + if (ability.getAbilityType().equals(AbilityType.MANA)) { + continue; + } List options = game.getPlayer(playerId).getPlayableOptions(ability, game); options = filterOptions(game, options, ability, suggested); options = optimizeOptions(game, options, ability); diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java index 5e5f1542bc5..4c008d09ff8 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java @@ -129,7 +129,7 @@ public class ArtificialScoringSystem { if (permanent.getCardType().contains(CardType.CREATURE)) { return -100; } else if (permanent.getCardType().contains(CardType.LAND)) { - return -1; + return -20; // means probably no mana available (should be greater than passivity penalty } else { return -2; } diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index f0bce292705..e10917f2746 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -160,6 +160,10 @@ import org.apache.log4j.Logger; public class ComputerPlayer extends PlayerImpl implements Player { private transient final static Logger log = Logger.getLogger(ComputerPlayer.class); + + protected int PASSIVITY_PENALTY = 5; // Penalty value for doing nothing if some actions are availble + protected boolean ALLOW_INTERRUPT = true; // change this for test purposes to switch off interrupts while debugging + private transient Map unplayable = new TreeMap<>(); private transient List playableNonInstant = new ArrayList<>(); private transient List playableInstant = new ArrayList<>(); @@ -171,7 +175,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { public ComputerPlayer(String name, RangeOfInfluence range) { super(name, range); human = false; - userData = new UserData(UserGroup.COMPUTER, 64, false, true, null); + userData = new UserData(UserGroup.COMPUTER, 64, false, true, false, null, "Computer.png"); pickedCards = new ArrayList<>(); } @@ -204,11 +208,16 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (log.isDebugEnabled()) { log.debug("chooseTarget: " + outcome.toString() + ":" + target.toString()); } - UUID opponentId = game.getOpponents(playerId).iterator().next(); + // sometimes a target aelection can be made from a player that does not control the ability + UUID abilityControllerId = playerId; + if (target.getTargetController() != null && target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); + } + UUID opponentId = game.getOpponents(abilityControllerId).iterator().next(); if (target instanceof TargetPlayer) { if (outcome.isGood()) { - if (target.canTarget(playerId, game)) { - target.add(playerId, game); + if (target.canTarget(abilityControllerId, game)) { + target.add(abilityControllerId, game); return true; } if (target.isRequired(sourceId, game)) { @@ -223,8 +232,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { return true; } if (target.isRequired(sourceId, game)) { - if (target.canTarget(playerId, game)) { - target.add(playerId, game); + if (target.canTarget(abilityControllerId, game)) { + target.add(abilityControllerId, game); return true; } } @@ -257,12 +266,12 @@ public class ComputerPlayer extends PlayerImpl implements Player { } if (target instanceof TargetControlledPermanent) { List targets; - targets = threats(playerId, sourceId, ((TargetControlledPermanent) target).getFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, sourceId, ((TargetControlledPermanent) target).getFilter(), game, target.getTargets()); if (!outcome.isGood()) { Collections.reverse(targets); } for (Permanent permanent : targets) { - if (((TargetControlledPermanent) target).canTarget(playerId, permanent.getId(), sourceId, game, false) && !target.getTargets().contains(permanent.getId())) { + if (((TargetControlledPermanent) target).canTarget(abilityControllerId, permanent.getId(), sourceId, game, false) && !target.getTargets().contains(permanent.getId())) { target.add(permanent.getId(), game); return true; } @@ -275,13 +284,20 @@ public class ComputerPlayer extends PlayerImpl implements Player { targets = threats(null, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); } else { if (outcome.isGood()) { - targets = threats(playerId, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); } else { targets = threats(opponentId, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); } + if (targets.isEmpty() && target.isRequired()) { + if (!outcome.isGood()) { + targets = threats(abilityControllerId, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); + } else { + targets = threats(opponentId, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); + } + } } for (Permanent permanent : targets) { - if (((TargetPermanent) target).canTarget(playerId, permanent.getId(), null, game) && !target.getTargets().contains(permanent.getId())) { + if (((TargetPermanent) target).canTarget(abilityControllerId, permanent.getId(), null, game) && !target.getTargets().contains(permanent.getId())) { target.add(permanent.getId(), game); return true; } @@ -309,13 +325,13 @@ public class ComputerPlayer extends PlayerImpl implements Player { List targets; TargetCreatureOrPlayer t = ((TargetCreatureOrPlayer) target); if (outcome.isGood()) { - targets = threats(playerId, sourceId, ((FilterCreatureOrPlayer) t.getFilter()).getCreatureFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, sourceId, ((FilterCreatureOrPlayer) t.getFilter()).getCreatureFilter(), game, target.getTargets()); } else { targets = threats(opponentId, sourceId, ((FilterCreatureOrPlayer) t.getFilter()).getCreatureFilter(), game, target.getTargets()); } for (Permanent permanent : targets) { List alreadyTargetted = target.getTargets(); - if (t.canTarget(playerId, permanent.getId(), null, game)) { + if (t.canTarget(abilityControllerId, permanent.getId(), null, game)) { if (alreadyTargetted != null && !alreadyTargetted.contains(permanent.getId())) { target.add(permanent.getId(), game); return true; @@ -323,8 +339,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } if (outcome.isGood()) { - if (target.canTarget(playerId, null, game)) { - target.add(playerId, game); + if (target.canTarget(abilityControllerId, null, game)) { + target.add(abilityControllerId, game); return true; } } else { @@ -341,7 +357,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (target instanceof TargetPermanentOrPlayer) { List targets; TargetPermanentOrPlayer t = ((TargetPermanentOrPlayer) target); - List ownedTargets = threats(playerId, sourceId, ((FilterPermanentOrPlayer) t.getFilter()).getPermanentFilter(), game, target.getTargets());; + List ownedTargets = threats(abilityControllerId, sourceId, ((FilterPermanentOrPlayer) t.getFilter()).getPermanentFilter(), game, target.getTargets());; List opponentTargets = threats(opponentId, sourceId, ((FilterPermanentOrPlayer) t.getFilter()).getPermanentFilter(), game, target.getTargets()); if (outcome.isGood()) { targets = ownedTargets; @@ -358,8 +374,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } if (outcome.isGood()) { - if (target.canTarget(playerId, null, game)) { - target.add(playerId, game); + if (target.canTarget(abilityControllerId, null, game)) { + target.add(abilityControllerId, game); return true; } } else { @@ -375,8 +391,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { target.add(opponentId, game); return true; } - if (target.canTarget(playerId, null, game)) { - target.add(playerId, game); + if (target.canTarget(abilityControllerId, null, game)) { + target.add(abilityControllerId, game); return true; } if (outcome.isGood()) { // no other valid targets so use a permanent @@ -415,7 +431,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (target instanceof TargetCardInYourGraveyard) { List alreadyTargetted = target.getTargets(); - List cards = new ArrayList<>(game.getPlayer(playerId).getGraveyard().getCards(game)); + List cards = new ArrayList<>(game.getPlayer(abilityControllerId).getGraveyard().getCards(game)); while(!cards.isEmpty()) { Card card = pickTarget(cards, outcome, target, null, game); if (card != null && alreadyTargetted != null && !alreadyTargetted.contains(card.getId())) { @@ -428,7 +444,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (target instanceof TargetSource) { Set targets; TargetSource t = ((TargetSource) target); - targets = t.possibleTargets(sourceId, playerId, game); + targets = t.possibleTargets(sourceId, abilityControllerId, game); for (UUID targetId : targets) { MageObject targetObject = game.getObject(targetId); if (targetObject != null) { @@ -455,28 +471,33 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (log.isDebugEnabled()) { log.debug("chooseTarget: " + outcome.toString() + ":" + target.toString()); } - UUID opponentId = game.getOpponents(playerId).iterator().next(); + // sometimes a target selection can be made from a player that does not control the ability + UUID abilityControllerId = playerId; + if (target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); + } + UUID opponentId = game.getOpponents(abilityControllerId).iterator().next(); if (target instanceof TargetPlayer) { if (outcome.isGood()) { - if (target.canTarget(playerId, playerId, source, game)) { + if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { target.addTarget(playerId, source, game); return true; } if (target.isRequired(source)) { - if (target.canTarget(playerId, opponentId, source, game)) { + if (target.canTarget(abilityControllerId, opponentId, source, game)) { target.addTarget(opponentId, source, game); return true; } } } else { - if (target.canTarget(playerId, opponentId, source, game)) { + if (target.canTarget(abilityControllerId, opponentId, source, game)) { target.addTarget(opponentId, source, game); return true; } if (target.isRequired(source)) { - if (target.canTarget(playerId, playerId, source, game)) { - target.addTarget(playerId, source, game); + if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { + target.addTarget(abilityControllerId, source, game); return true; } } @@ -526,12 +547,12 @@ public class ComputerPlayer extends PlayerImpl implements Player { } if (target instanceof TargetControlledPermanent) { List targets; - targets = threats(playerId, source.getSourceId(), ((TargetControlledPermanent)target).getFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, source.getSourceId(), ((TargetControlledPermanent)target).getFilter(), game, target.getTargets()); if (!outcome.isGood()) { Collections.reverse(targets); } for (Permanent permanent: targets) { - if (((TargetControlledPermanent)target).canTarget(playerId, permanent.getId(), source, game)) { + if (((TargetControlledPermanent)target).canTarget(abilityControllerId, permanent.getId(), source, game)) { target.addTarget(permanent.getId(), source, game); if (target.getNumberOfTargets() <= target.getTargets().size() && (!outcome.isGood() || target.getMaxNumberOfTargets() <= target.getTargets().size())) { return true; @@ -545,7 +566,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { List targets; boolean outcomeTargets = true; if (outcome.isGood()) { - targets = threats(playerId, source == null?null:source.getSourceId(), ((TargetPermanent)target).getFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, source == null?null:source.getSourceId(), ((TargetPermanent)target).getFilter(), game, target.getTargets()); } else { targets = threats(opponentId, source == null?null:source.getSourceId(), ((TargetPermanent)target).getFilter(), game, target.getTargets()); @@ -557,7 +578,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { //targets = game.getBattlefield().getActivePermanents(((TargetPermanent)target).getFilter(), playerId, game); } for (Permanent permanent: targets) { - if (((TargetPermanent)target).canTarget(playerId, permanent.getId(), source, game)) { + if (((TargetPermanent)target).canTarget(abilityControllerId, permanent.getId(), source, game)) { target.addTarget(permanent.getId(), source, game); if (!outcomeTargets || target.getMaxNumberOfTargets() <= target.getTargets().size()) { return true; @@ -570,7 +591,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { List targets; TargetCreatureOrPlayer t = ((TargetCreatureOrPlayer)target); if (outcome.isGood()) { - targets = threats(playerId, source.getSourceId(), ((FilterCreatureOrPlayer)t.getFilter()).getCreatureFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, source.getSourceId(), ((FilterCreatureOrPlayer)t.getFilter()).getCreatureFilter(), game, target.getTargets()); } else { targets = threats(opponentId, source.getSourceId(), ((FilterCreatureOrPlayer)t.getFilter()).getCreatureFilter(), game, target.getTargets()); @@ -578,8 +599,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (targets.isEmpty()) { if (outcome.isGood()) { - if (target.canTarget(playerId, source, game)) { - target.addTarget(playerId, source, game); + if (target.canTarget(abilityControllerId, source, game)) { + target.addTarget(abilityControllerId, source, game); return true; } } @@ -596,7 +617,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } for (Permanent permanent : targets) { List alreadyTargetted = target.getTargets(); - if (t.canTarget(playerId, permanent.getId(), source, game)) { + if (t.canTarget(abilityControllerId, permanent.getId(), source, game)) { if (alreadyTargetted != null && !alreadyTargetted.contains(permanent.getId())) { target.addTarget(permanent.getId(), source, game); return true; @@ -605,8 +626,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { } if (outcome.isGood()) { - if (target.canTarget(playerId, source, game)) { - target.addTarget(playerId, source, game); + if (target.canTarget(abilityControllerId, source, game)) { + target.addTarget(abilityControllerId, source, game); return true; } } @@ -634,7 +655,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { return false; } if (target instanceof TargetCardInLibrary) { - List cards = new ArrayList<>(game.getPlayer(playerId).getLibrary().getCards(game)); + List cards = new ArrayList<>(game.getPlayer(abilityControllerId).getLibrary().getCards(game)); Card card = pickTarget(cards, outcome, target, source, game); if (card != null) { target.addTarget(card.getId(), source, game); @@ -643,7 +664,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { return false; } if (target instanceof TargetCardInYourGraveyard) { - List cards = new ArrayList<>(game.getPlayer(playerId).getGraveyard().getCards(game)); + List cards = new ArrayList<>(game.getPlayer(abilityControllerId).getGraveyard().getCards(game)); while(!target.isChosen() && !cards.isEmpty()) { Card card = pickTarget(cards, outcome, target, source, game); if (card != null) { @@ -680,7 +701,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { List targets; boolean outcomeTargets = true; if (outcome.isGood()) { - targets = threats(playerId, source == null?null:source.getSourceId(), ((TargetSpellOrPermanent)target).getPermanentFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, source == null?null:source.getSourceId(), ((TargetSpellOrPermanent)target).getPermanentFilter(), game, target.getTargets()); } else { targets = threats(opponentId, source == null?null:source.getSourceId(), ((TargetSpellOrPermanent)target).getPermanentFilter(), game, target.getTargets()); @@ -692,7 +713,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { //targets = game.getBattlefield().getActivePermanents(((TargetPermanent)target).getFilter(), playerId, game); } for (Permanent permanent: targets) { - if (((TargetPermanent)target).canTarget(playerId, permanent.getId(), source, game)) { + if (((TargetPermanent)target).canTarget(abilityControllerId, permanent.getId(), source, game)) { target.addTarget(permanent.getId(), source, game); if (!outcomeTargets || target.getMaxNumberOfTargets() <= target.getTargets().size()) { return true; @@ -715,7 +736,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } if (target instanceof TargetCardInOpponentsGraveyard) { List cards = new ArrayList<>(); - for (UUID uuid: game.getOpponents(playerId)) { + for (UUID uuid: game.getOpponents(abilityControllerId)) { Player player = game.getPlayer(uuid); if (player != null) { cards.addAll(player.getGraveyard().getCards(game)); @@ -951,9 +972,11 @@ public class ComputerPlayer extends PlayerImpl implements Player { @Override public boolean activateAbility(ActivatedAbility ability, Game game) { - for (Target target: ability.getModes().getMode().getTargets()) { - for (UUID targetId: target.getTargets()) { - game.fireEvent(GameEvent.getEvent(EventType.TARGETED, targetId, ability.getId(), ability.getControllerId())); + if (!isTestMode()) { // Test player already sends target event as he selects the target + for (Target target: ability.getModes().getMode().getTargets()) { + for (UUID targetId: target.getTargets()) { + game.fireEvent(GameEvent.getEvent(EventType.TARGETED, targetId, ability.getId(), ability.getControllerId())); + } } } return super.activateAbility(ability, game); @@ -1572,7 +1595,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } @Override - protected List getAvailableManaProducers(Game game) { + public List getAvailableManaProducers(Game game) { return super.getAvailableManaProducers(game); } diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 5a3750c595a..77bb838f1de 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -68,6 +68,7 @@ import org.apache.log4j.Logger; import java.io.Serializable; import java.util.*; +import mage.util.GameLog; /** * @@ -235,13 +236,17 @@ public class HumanPlayer extends PlayerImpl { @Override public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map options) { updateGameStatePriority("choose(5)", game); + UUID abilityControllerId = playerId; + if (target.getTargetController() != null && target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); + } if (options == null) { options = new HashMap<>(); } while (!abort) { - Set targetIds = target.possibleTargets(sourceId, playerId, game); - if (targetIds == null || targetIds.isEmpty()) { - return false; + Set targetIds = target.possibleTargets(sourceId, abilityControllerId, game); + if (targetIds == null || targetIds.isEmpty()) { + return target.getTargets().size() >= target.getNumberOfTargets(); } boolean required = target.isRequired(sourceId, game); if (target.getTargets().size() >= target.getNumberOfTargets()) { @@ -251,14 +256,14 @@ public class HumanPlayer extends PlayerImpl { List chosen = target.getTargets(); options.put("chosen", (Serializable)chosen); - game.fireSelectTargetEvent(playerId, target.getMessage(), targetIds, required, getOptions(target, options)); + game.fireSelectTargetEvent(getId(), target.getMessage(), targetIds, required, getOptions(target, options)); waitForResponse(game); if (response.getUUID() != null) { if (!targetIds.contains(response.getUUID())) { continue; } if (target instanceof TargetPermanent) { - if (((TargetPermanent)target).canTarget(playerId, response.getUUID(), sourceId, game, false)) { + if (((TargetPermanent)target).canTarget(abilityControllerId, response.getUUID(), sourceId, game, false)) { target.add(response.getUUID(), game); if(target.doneChosing()){ return true; @@ -306,13 +311,17 @@ public class HumanPlayer extends PlayerImpl { @Override public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) { updateGameStatePriority("chooseTarget", game); + UUID abilityControllerId = playerId; + if (target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); + } while (!abort) { - Set possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), playerId, game); + Set possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), abilityControllerId, game); boolean required = target.isRequired(source); if (possibleTargets.isEmpty() || target.getTargets().size() >= target.getNumberOfTargets()) { required = false; } - game.fireSelectTargetEvent(playerId, target.getMessage(), possibleTargets, required, getOptions(target, null)); + game.fireSelectTargetEvent(getId(), target.getMessage(), possibleTargets, required, getOptions(target, null)); waitForResponse(game); if (response.getUUID() != null) { if (target.getTargets().contains(response.getUUID())) { @@ -320,7 +329,7 @@ public class HumanPlayer extends PlayerImpl { continue; } if (possibleTargets.contains(response.getUUID())) { - if (target.canTarget(playerId, response.getUUID(), source, game)) { + if (target.canTarget(abilityControllerId, response.getUUID(), source, game)) { target.addTarget(response.getUUID(), source, game); if(target.doneChosing()){ return true; @@ -486,31 +495,35 @@ public class HumanPlayer extends PlayerImpl { passed = false; if (!abort) { if (passedAllTurns) { - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } if (game.getStack().isEmpty()) { passedUntilStackResolved = false; boolean dontCheckPassStep = false; if (passedTurn) { - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } if (passedUntilNextMain) { if (game.getTurn().getStepType().equals(PhaseStep.POSTCOMBAT_MAIN) || game.getTurn().getStepType().equals(PhaseStep.PRECOMBAT_MAIN)) { // it's a main phase if (!skippedAtLeastOnce || (!playerId.equals(game.getActivePlayerId()) && !this.getUserData().getUserSkipPrioritySteps().isStopOnAllMainPhases())) { skippedAtLeastOnce = true; - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } else { dontCheckPassStep = true; passedUntilNextMain = false; // reset skip action } } else { skippedAtLeastOnce = true; - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } } if (passedUntilEndOfTurn) { @@ -518,45 +531,53 @@ public class HumanPlayer extends PlayerImpl { // It's end of turn phase if (!skippedAtLeastOnce || (playerId.equals(game.getActivePlayerId()) && !this.getUserData().getUserSkipPrioritySteps().isStopOnAllEndPhases())) { skippedAtLeastOnce = true; - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } else { dontCheckPassStep = true; passedUntilEndOfTurn = false; } } else { skippedAtLeastOnce = true; - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } } if (!dontCheckPassStep && checkPassStep(game)) { - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } } else if (passedUntilStackResolved) { if (dateLastAddedToStack == game.getStack().getDateLastAdded()) { dateLastAddedToStack = game.getStack().getDateLastAdded(); - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } else { passedUntilStackResolved = false; } } - updateGameStatePriority("priority", game); - game.firePriorityEvent(playerId); - waitForResponse(game); - if (response.getBoolean() != null) { - pass(game); - return false; - } else if (response.getInteger() != null) { - /*if (response.getInteger() == -9999) { - passedAllTurns = true; - }*/ - pass(game); - //passedTurn = true; - return false; - } else if (response.getString() != null && response.getString().equals("special")) { + while (isInGame()) { + updateGameStatePriority("priority", game); + game.firePriorityEvent(playerId); + waitForResponse(game); + if(game.executingRollback()) { + return true; + } + if (response.getBoolean() != null || response.getInteger() != null) { + if (passWithManaPoolCheck(game)) { + return false; + } else { + continue; + } + } + break; + } + + if (response.getString() != null && response.getString().equals("special")) { specialAction(game); } else if (response.getUUID() != null) { boolean result = false; @@ -1017,6 +1038,12 @@ public class HumanPlayer extends PlayerImpl { } } + @Override + public boolean activateAbility(ActivatedAbility ability, Game game) { + getManaPool().setStock(); // needed for the "mana already in the pool has to be used manually" option + return super.activateAbility(ability, game); + } + protected void activateAbility(LinkedHashMap abilities, MageObject object, Game game) { updateGameStatePriority("activateAbility", game); if (abilities.size() == 1 && suppressAbilityPicker(abilities.values().iterator().next())) { @@ -1205,4 +1232,27 @@ public class HumanPlayer extends PlayerImpl { super.sendPlayerAction(playerAction, game); } } + + protected boolean passWithManaPoolCheck(Game game) { + if (userData.confirmEmptyManaPool() && + game.getStack().isEmpty() && getManaPool().count() > 0) { + String activePlayerText; + if (game.getActivePlayerId().equals(playerId)) { + activePlayerText = "Your turn"; + } else { + activePlayerText = game.getPlayer(game.getActivePlayerId()).getName() + "'s turn"; + } + String priorityPlayerText = ""; + if (!isGameUnderControl()) { + priorityPlayerText = " / priority " + game.getPlayer(game.getPriorityPlayerId()).getName(); + } + if (!chooseUse(Outcome.Detriment, GameLog.getPlayerConfirmColoredText("You have still mana in your mana pool. Pass regardless?") + + GameLog.getSmallSecondLineText(activePlayerText + " / " + game.getStep().getType().toString() + priorityPlayerText), game)) { + sendPlayerAction(PlayerAction.PASS_PRIORITY_CANCEL_ALL_ACTIONS, game); + return false; + } + } + pass(game); + return true; + } } diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index 00801348034..90153afbe9f 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -157,7 +157,7 @@ public class Session { if (user == null) { user = UserManager.getInstance().findUser("Admin"); } - user.setUserData(new UserData(UserGroup.ADMIN, 0, false, false, null)); + user.setUserData(new UserData(UserGroup.ADMIN, 0, false, false, false, null, "world.png")); if (!UserManager.getInstance().connectToSession(sessionId, user.getId())) { logger.info("Error connecting Admin!"); } @@ -169,7 +169,10 @@ public class Session { if (user != null) { UserData userData = user.getUserData(); if (userData == null) { - userData = new UserData(UserGroup.PLAYER, userDataView.getAvatarId(), userDataView.isShowAbilityPickerForced(), userDataView.allowRequestShowHandCards(), userDataView.getUserSkipPrioritySteps()); + userData = new UserData(UserGroup.PLAYER, userDataView.getAvatarId(), + userDataView.isShowAbilityPickerForced(), userDataView.allowRequestShowHandCards(), + userDataView.confirmEmptyManaPool(), userDataView.getUserSkipPrioritySteps(), + userDataView.getFlagName()); user.setUserData(userData); } else { if (userDataView.getAvatarId() == 51) { // Update special avatar if first avatar is selected @@ -179,6 +182,7 @@ public class Session { userData.setShowAbilityPickerForced(userDataView.isShowAbilityPickerForced()); userData.setAllowRequestShowHandCards(userDataView.allowRequestShowHandCards()); userData.setUserSkipPrioritySteps(userDataView.getUserSkipPrioritySteps()); + userData.setConfirmEmptyManaPool(userDataView.confirmEmptyManaPool()); } return true; } diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index 826e85b2ba8..b7f7df7d54a 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -43,6 +43,7 @@ import mage.constants.RangeOfInfluence; import mage.constants.TableState; import mage.game.Game; import mage.game.GameException; +import mage.game.GameOptions; import mage.game.Seat; import mage.game.Table; import mage.game.draft.Draft; @@ -570,7 +571,10 @@ public class TableController { try { match.startGame(); table.initGame(); - GameManager.getInstance().createGameSession(match.getGame(), userPlayerMap, table.getId(), choosingPlayerId); + GameOptions gameOptions = new GameOptions(); + gameOptions.rollbackTurnsAllowed = match.getOptions().isRollbackTurnsAllowed(); + match.getGame().setGameOptions(gameOptions); + GameManager.getInstance().createGameSession(match.getGame(), userPlayerMap, table.getId(), choosingPlayerId, gameOptions); String creator = null; StringBuilder opponent = new StringBuilder(); for (Entry entry: userPlayerMap.entrySet()) { // no AI players diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index aecc0affbcb..3b94b58f79d 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -64,6 +64,7 @@ import mage.constants.PlayerAction; import mage.constants.Zone; import mage.game.Game; import mage.game.GameException; +import mage.game.GameOptions; import mage.game.Table; import mage.game.events.Listener; import mage.game.events.PlayerQueryEvent; @@ -118,15 +119,23 @@ public class GameController implements GameCallback { private UUID choosingPlayerId; private Future gameFuture; private boolean useTimeout = true; + private GameOptions gameOptions; + + private UUID userReqestingRollback; + private int turnsToRollback; + private int requestsOpen; + - public GameController(Game game, ConcurrentHashMap userPlayerMap, UUID tableId, UUID choosingPlayerId) { + public GameController(Game game, ConcurrentHashMap userPlayerMap, UUID tableId, UUID choosingPlayerId, GameOptions gameOptions) { gameSessionId = UUID.randomUUID(); this.userPlayerMap = userPlayerMap; chatId = ChatManager.getInstance().createChatSession("Game " + game.getId()); + this.userReqestingRollback = null; this.game = game; this.game.setSaveGame(ConfigSettings.getInstance().isSaveGameActivated()); this.tableId = tableId; this.choosingPlayerId = choosingPlayerId; + this.gameOptions = gameOptions; for (Player player: game.getPlayers().values()) { if (!player.isHuman()) { useTimeout = false; // no timeout for AI players because of beeing idle @@ -477,14 +486,70 @@ public class GameController implements GameCallback { case UNDO: game.undo(getPlayerId(userId)); break; + case ROLLBACK_TURNS: // basic request of a player to rollback + if (data instanceof Integer) { + turnsToRollback = (Integer) data; + if (game.canRollbackTurns(turnsToRollback)) { + requestsOpen = requestPermissionToRollback(userId, turnsToRollback); + if (requestsOpen == 0) { + game.rollbackTurns(turnsToRollback); + turnsToRollback = -1; + requestsOpen = -1; + } else { + userReqestingRollback = userId; + } + } else { + UUID playerId = getPlayerId(userId); + if (playerId != null) { + Player player = game.getPlayer(playerId); + if (player != null) { + game.informPlayer(player, "That turn is not available for rollback."); + } + } + } + } + break; + case ADD_PERMISSION_TO_ROLLBACK_TURN: + if (userReqestingRollback != null && requestsOpen > 0 && !userId.equals(userReqestingRollback)) { + requestsOpen--; + if (requestsOpen == 0) { + game.rollbackTurns(turnsToRollback); + turnsToRollback = -1; + userReqestingRollback = null; + requestsOpen = -1; + } + } + break; + case DENY_PERMISSON_TO_ROLLBACK_TURN: // one player has denied - so cancel the request + { + UUID playerId = getPlayerId(userId); + if (playerId != null) { + Player player = game.getPlayer(playerId); + if (player != null) { + if (userReqestingRollback != null && requestsOpen > 0 && !userId.equals(userReqestingRollback)) { + turnsToRollback = -1; + userReqestingRollback = null; + requestsOpen = -1; + game.informPlayers("Rollback request denied by " + player.getLogName()); + } + } + } + } + break; case CONCEDE: game.concede(getPlayerId(userId)); break; case MANA_AUTO_PAYMENT_OFF: - game.setManaPoolMode(getPlayerId(userId), false); + game.setManaPaymentMode(getPlayerId(userId), false); break; case MANA_AUTO_PAYMENT_ON: - game.setManaPoolMode(getPlayerId(userId), true); + game.setManaPaymentMode(getPlayerId(userId), true); + break; + case MANA_AUTO_PAYMENT_RESTRICTED_OFF: + game.setManaPaymentModeRestricted(getPlayerId(userId), false); + break; + case MANA_AUTO_PAYMENT_RESTRICTED_ON: + game.setManaPaymentModeRestricted(getPlayerId(userId), true); break; case ADD_PERMISSION_TO_SEE_HAND_CARDS: if (data instanceof UUID) { @@ -516,6 +581,23 @@ public class GameController implements GameCallback { } } + private int requestPermissionToRollback(UUID userIdRequester, int numberTurns) { + int requests = 0; + for (Player player: game.getState().getPlayers().values()) { + User requestedUser = getUserByPlayerId(player.getId()); + if (player.isInGame() && player.isHuman() && + requestedUser != null && + !requestedUser.getId().equals(userIdRequester)) { + requests++; + GameSessionPlayer gameSession = gameSessions.get(player.getId()); + if (gameSession != null) { + gameSession.requestPermissionToRollbackTurn(userIdRequester, numberTurns); + } + } + } + return requests; + } + private void requestPermissionToSeeHandCards(UUID userIdRequester, UUID userIdGranter) { Player grantingPlayer = game.getPlayer(userIdGranter); if (grantingPlayer != null) { diff --git a/Mage.Server/src/main/java/mage/server/game/GameManager.java b/Mage.Server/src/main/java/mage/server/game/GameManager.java index 7075ea8e078..0b4b2e9c37e 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameManager.java +++ b/Mage.Server/src/main/java/mage/server/game/GameManager.java @@ -34,6 +34,7 @@ import mage.cards.decks.DeckCardLists; import mage.constants.ManaType; import mage.constants.PlayerAction; import mage.game.Game; +import mage.game.GameOptions; import mage.view.GameView; /** @@ -51,8 +52,8 @@ public class GameManager { private final ConcurrentHashMap gameControllers = new ConcurrentHashMap<>(); - public UUID createGameSession(Game game, ConcurrentHashMap userPlayerMap, UUID tableId, UUID choosingPlayerId) { - GameController gameController = new GameController(game, userPlayerMap, tableId, choosingPlayerId); + public UUID createGameSession(Game game, ConcurrentHashMap userPlayerMap, UUID tableId, UUID choosingPlayerId, GameOptions gameOptions) { + GameController gameController = new GameController(game, userPlayerMap, tableId, choosingPlayerId, gameOptions); gameControllers.put(game.getId(), gameController); return gameController.getSessionId(); } diff --git a/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java b/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java index e2cd374f6f4..0b43412b383 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java +++ b/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java @@ -170,6 +170,34 @@ public class GameSessionPlayer extends GameSessionWatcher { } } + public void requestPermissionToRollbackTurn(UUID requestingUserId, int numberTurns) { + if (!killed) { + User requestingUser = UserManager.getInstance().getUser(requestingUserId); + User requestedUser = UserManager.getInstance().getUser(userId); + if (requestedUser != null && requestingUser != null) { + String message; + switch(numberTurns) { + case 0: + message = "Allow rollback to the start of the current turn?"; + break; + case 1: + message = "Allow rollback to the start of the previous turn?"; + break; + default: + message = "Allow to rollback "+numberTurns+ " turns?"; + } + UserRequestMessage userRequestMessage = new UserRequestMessage( + "Request by " + requestedUser.getName(), message + , PlayerAction.REQUEST_PERMISSION_TO_ROLLBACK_TURN); + userRequestMessage.setRelatedUser(requestingUserId, requestingUser.getName()); + userRequestMessage.setGameId(game.getId()); + userRequestMessage.setButton1("Accept", PlayerAction.ADD_PERMISSION_TO_ROLLBACK_TURN); + userRequestMessage.setButton2("Deny", PlayerAction.DENY_PERMISSON_TO_ROLLBACK_TURN); + requestedUser.fireCallback(new ClientCallback("userRequestDialog", game.getId(), userRequestMessage)); + } + } + } + public void requestPermissionToSeeHandCards(UUID watcherId) { if (!killed) { User watcher = UserManager.getInstance().getUser(watcherId); @@ -237,7 +265,7 @@ public class GameSessionPlayer extends GameSessionWatcher { Map handCards = new HashMap<>(); for (UUID controlledPlayerId : player.getPlayersUnderYourControl()) { Player opponent = game.getPlayer(controlledPlayerId); - handCards.put(opponent.getName(), new SimpleCardsView(opponent.getHand().getCards(game))); + handCards.put(opponent.getName(), new SimpleCardsView(opponent.getHand().getCards(game), true)); } gameView.setOpponentHands(handCards); } diff --git a/Mage.Server/src/main/java/mage/server/game/GameSessionWatcher.java b/Mage.Server/src/main/java/mage/server/game/GameSessionWatcher.java index f269b87f0e3..e1db1e08d25 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameSessionWatcher.java +++ b/Mage.Server/src/main/java/mage/server/game/GameSessionWatcher.java @@ -142,7 +142,7 @@ public class GameSessionWatcher { Map handCards = new HashMap<>(); for (Player player: game.getPlayers().values()) { if (player.hasUserPermissionToSeeHand(userId)) { - handCards.put(player.getName(), new SimpleCardsView(player.getHand().getCards(game))); + handCards.put(player.getName(), new SimpleCardsView(player.getHand().getCards(game), true)); gameView.setWatchedHands(handCards); } } diff --git a/Mage.Server/src/main/java/mage/server/game/GameWorker.java b/Mage.Server/src/main/java/mage/server/game/GameWorker.java index 7a1ae9753c3..50ec2f75b95 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameWorker.java +++ b/Mage.Server/src/main/java/mage/server/game/GameWorker.java @@ -37,6 +37,7 @@ import org.apache.log4j.Logger; /** * * @author BetaSteward_at_googlemail.com + * @param */ public class GameWorker implements Callable { diff --git a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java index 765b6a09be4..c97251cd3d4 100644 --- a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java +++ b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java @@ -123,10 +123,17 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable { for (User user : UserManager.getInstance().getUsers()) { Session session = SessionManager.getInstance().getSession(user.getSessionId()); try { +<<<<<<< HEAD users.add(new UsersView(user.getName(), user.getInfo(), user.getGameInfo(), session.getPingInfo())); } catch (Exception ex) { logger.fatal("User update exception: " + user.getName() + " - " + ex.toString(), ex); users.add(new UsersView(user.getName(), user.getInfo(), "[exception]", session.getPingInfo())); +======= + users.add(new UsersView(user.getUserData().getFlagName(), user.getName(), user.getInfo(), user.getGameInfo(), user.getPingInfo())); + } catch (Exception ex) { + logger.fatal("User update exception: " + user.getName() + " - " + ex.toString(), ex); + users.add(new UsersView(user.getUserData().getFlagName(), user.getName(), user.getInfo(), "[exception]", user.getPingInfo())); +>>>>>>> master } } diff --git a/Mage.Sets/src/mage/sets/Conspiracy.java b/Mage.Sets/src/mage/sets/Conspiracy.java index cb0ad96e7c7..1cf7af1408a 100644 --- a/Mage.Sets/src/mage/sets/Conspiracy.java +++ b/Mage.Sets/src/mage/sets/Conspiracy.java @@ -37,7 +37,6 @@ import mage.constants.SetType; * @author LevelX2 */ - public class Conspiracy extends ExpansionSet { private static final Conspiracy fINSTANCE = new Conspiracy(); diff --git a/Mage.Sets/src/mage/sets/alarareborn/GloryscaleViashino.java b/Mage.Sets/src/mage/sets/alarareborn/GloryscaleViashino.java index 87a8081830f..46d20e93bba 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/GloryscaleViashino.java +++ b/Mage.Sets/src/mage/sets/alarareborn/GloryscaleViashino.java @@ -89,7 +89,7 @@ class GloryscaleViashinoAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isMulticolored() && event.getPlayerId().equals(getControllerId())) { + if (spell != null && spell.getColor(game).isMulticolored() && event.getPlayerId().equals(getControllerId())) { return true; } } diff --git a/Mage.Sets/src/mage/sets/alarareborn/KnightOfNewAlara.java b/Mage.Sets/src/mage/sets/alarareborn/KnightOfNewAlara.java index 8505d0f946e..51ba166beb6 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/KnightOfNewAlara.java +++ b/Mage.Sets/src/mage/sets/alarareborn/KnightOfNewAlara.java @@ -102,7 +102,7 @@ class KnightOfNewAlaraEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { for (Permanent creature : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { if (creature != null && !creature.getId().equals(source.getSourceId())) { - int colors = creature.getColor().getColorCount(); + int colors = creature.getColor(game).getColorCount(); creature.addPower(colors); creature.addToughness(colors); } diff --git a/Mage.Sets/src/mage/sets/alarareborn/NemesisOfReason.java b/Mage.Sets/src/mage/sets/alarareborn/NemesisOfReason.java index ffd7501ad19..276295c2860 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/NemesisOfReason.java +++ b/Mage.Sets/src/mage/sets/alarareborn/NemesisOfReason.java @@ -53,11 +53,14 @@ public class NemesisOfReason extends CardImpl { this.expansionSetCode = "ARB"; this.subtype.add("Leviathan"); this.subtype.add("Horror"); - this.power = new MageInt(3); this.toughness = new MageInt(7); - this.addAbility(new NemesisOfReasonTriggeredAbility()); + + // Whenever Nemesis of Reason attacks, defending player puts the top ten cards of his or her library into his or her graveyard. + Effect effect = new PutLibraryIntoGraveTargetEffect(10); + effect.setText("defending player puts the top ten cards of his or her library into his or her graveyard"); + this.addAbility(new NemesisOfReasonTriggeredAbility(effect)); } public NemesisOfReason (final NemesisOfReason card) { @@ -71,8 +74,9 @@ public class NemesisOfReason extends CardImpl { } class NemesisOfReasonTriggeredAbility extends TriggeredAbilityImpl { - NemesisOfReasonTriggeredAbility() { - super(Zone.BATTLEFIELD, new PutLibraryIntoGraveTargetEffect(10)); + + NemesisOfReasonTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect); } NemesisOfReasonTriggeredAbility(final NemesisOfReasonTriggeredAbility ability) { @@ -84,11 +88,17 @@ class NemesisOfReasonTriggeredAbility extends TriggeredAbilityImpl { return new NemesisOfReasonTriggeredAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ATTACKER_DECLARED; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED && event.getSourceId().equals(this.getSourceId()) ) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + if (event.getSourceId().equals(this.getSourceId()) ) { + UUID defenderId = game.getCombat().getDefendingPlayerId(this.getSourceId(), game); + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(defenderId)); } return true; } diff --git a/Mage.Sets/src/mage/sets/alarareborn/QasaliPridemage.java b/Mage.Sets/src/mage/sets/alarareborn/QasaliPridemage.java index bccf5694cd9..392f4efd5cb 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/QasaliPridemage.java +++ b/Mage.Sets/src/mage/sets/alarareborn/QasaliPridemage.java @@ -39,9 +39,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.ExaltedAbility; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.Target; import mage.target.TargetPermanent; @@ -51,22 +49,11 @@ import mage.target.TargetPermanent; */ public class QasaliPridemage extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public QasaliPridemage(UUID ownerId) { super(ownerId, 75, "Qasali Pridemage", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{G}{W}"); this.expansionSetCode = "ARB"; this.subtype.add("Cat"); this.subtype.add("Wizard"); - - - this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -76,7 +63,7 @@ public class QasaliPridemage extends CardImpl { // {1}, Sacrifice Qasali Pridemage: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{1}")); ability.addCost(new SacrificeSourceCost()); - Target target = new TargetPermanent(filter); + Target target = new TargetPermanent(new FilterArtifactOrEnchantmentPermanent()); ability.addTarget(target); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/alarareborn/Thraximundar.java b/Mage.Sets/src/mage/sets/alarareborn/Thraximundar.java index ac23550a8a3..3028eded661 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/Thraximundar.java +++ b/Mage.Sets/src/mage/sets/alarareborn/Thraximundar.java @@ -108,10 +108,14 @@ class ThraximundarTriggeredAbility extends TriggeredAbilityImpl { return new ThraximundarTriggeredAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ATTACKER_DECLARED; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED - && event.getSourceId() == this.getSourceId()) { + if (event.getSourceId() == this.getSourceId()) { UUID defender = game.getCombat().getDefendingPlayerId(this.getSourceId(), game); this.getEffects().get(0).setTargetPointer(new FixedTarget(defender)); return true; diff --git a/Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java b/Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java index 4f861edd823..e315a53fe22 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java +++ b/Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java @@ -34,7 +34,6 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.constants.CardType; @@ -58,14 +57,11 @@ public class VectisDominator extends CardImpl { this.subtype.add("Human"); this.subtype.add("Wizard"); - - this.power = new MageInt(0); this.toughness = new MageInt(2); - // {tap}: Tap target creature unless its controller pays 2 life. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new VectisDominatorEffect(new PayLifeCost(2)), new ManaCostsImpl("{1}")); - ability.addCost(new TapSourceCost()); + // {T}: Tap target creature unless its controller pays 2 life. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new VectisDominatorEffect(new PayLifeCost(2)), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } @@ -107,7 +103,7 @@ class VectisDominatorEffect extends OneShotEffect { Player player = game.getPlayer(targetCreature.getControllerId()); if (player != null) { cost.clearPaid(); - final StringBuilder sb = new StringBuilder("Pay 2 life otherwise ").append(targetCreature.getName()).append(" will be tapped)"); + final StringBuilder sb = new StringBuilder("Pay 2 life? (Otherwise ").append(targetCreature.getName()).append(" will be tapped)"); if (player.chooseUse(Outcome.Benefit, sb.toString(), game)) { cost.pay(source, game, targetCreature.getControllerId(), targetCreature.getControllerId(), true); } diff --git a/Mage.Sets/src/mage/sets/alarareborn/VithianRenegades.java b/Mage.Sets/src/mage/sets/alarareborn/VithianRenegades.java index 292651c14d8..3f693142a46 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/VithianRenegades.java +++ b/Mage.Sets/src/mage/sets/alarareborn/VithianRenegades.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.alarareborn; import java.util.UUID; @@ -36,8 +35,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -45,11 +43,6 @@ import mage.target.TargetPermanent; * @author Loki */ public class VithianRenegades extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } public VithianRenegades (UUID ownerId) { super(ownerId, 64, "Vithian Renegades", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{R}{G}"); @@ -57,11 +50,12 @@ public class VithianRenegades extends CardImpl { this.subtype.add("Human"); this.subtype.add("Shaman"); - this.power = new MageInt(3); this.toughness = new MageInt(2); + + // When Vithian Renegades enters the battlefield, destroy target artifact. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactPermanent())); this.addAbility(ability); } @@ -73,5 +67,4 @@ public class VithianRenegades extends CardImpl { public VithianRenegades copy() { return new VithianRenegades(this); } - } diff --git a/Mage.Sets/src/mage/sets/alliances/HelmOfObedience.java b/Mage.Sets/src/mage/sets/alliances/HelmOfObedience.java index 6f3768f2913..76325f76e42 100644 --- a/Mage.Sets/src/mage/sets/alliances/HelmOfObedience.java +++ b/Mage.Sets/src/mage/sets/alliances/HelmOfObedience.java @@ -97,32 +97,32 @@ class HelmOfObedienceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { + Player targetOpponent = game.getPlayer(targetPointer.getFirst(game, source)); + if (targetOpponent != null) { int max = amount.calculate(game, source, this); if(max != 0){ int numberOfCard = 0; - while(player.getLibrary().size() > 0) { - Card card = player.getLibrary().removeFromTop(game); + while(targetOpponent.getLibrary().size() > 0) { + Card card = targetOpponent.getLibrary().removeFromTop(game); if (card != null){ - player.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); - if(card.getCardType().contains(CardType.CREATURE)){ - // If a creature card is put into that graveyard this way, sacrifice Helm of Obedience - // and put that card onto the battlefield under your control. - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (sourcePermanent != null) { - sourcePermanent.sacrifice(source.getSourceId(), game); - } - if (game.getState().getZone(card.getId()).equals(Zone.GRAVEYARD)) { - card.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId()); - } - break; - } - else{ - numberOfCard++; - if(numberOfCard >= max){ + if (targetOpponent.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game)) { + if(card.getCardType().contains(CardType.CREATURE)){ + // If a creature card is put into that graveyard this way, sacrifice Helm of Obedience + // and put that card onto the battlefield under your control. + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + if (sourcePermanent != null) { + sourcePermanent.sacrifice(source.getSourceId(), game); + } + if (game.getState().getZone(card.getId()).equals(Zone.GRAVEYARD)) { + card.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId()); + } break; + } else{ + numberOfCard++; + if(numberOfCard >= max){ + break; + } } } } else{ diff --git a/Mage.Sets/src/mage/sets/antiquities/ArtifactBlast.java b/Mage.Sets/src/mage/sets/antiquities/ArtifactBlast.java new file mode 100644 index 00000000000..56d1cc45fe1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/antiquities/ArtifactBlast.java @@ -0,0 +1,60 @@ +/* + * 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.sets.antiquities; + +import java.util.UUID; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.target.TargetSpell; +import mage.filter.common.FilterArtifactSpell; + +/** + * + * @author Jgod + */ +public class ArtifactBlast extends CardImpl { + public ArtifactBlast(UUID ownerId) { + super(ownerId, 87, "Artifact Blast", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}"); + this.expansionSetCode = "ATQ"; + + // Counter target artifact spell. + this.getSpellAbility().addTarget(new TargetSpell(new FilterArtifactSpell())); + this.getSpellAbility().addEffect(new CounterTargetEffect()); + } + + public ArtifactBlast(final ArtifactBlast card) { + super(card); + } + + @Override + public ArtifactBlast copy() { + return new ArtifactBlast(this); + } +} diff --git a/Mage.Sets/src/mage/sets/antiquities/ClayStatue.java b/Mage.Sets/src/mage/sets/antiquities/ClayStatue.java new file mode 100644 index 00000000000..78636b4a4f3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/antiquities/ClayStatue.java @@ -0,0 +1,38 @@ +package mage.sets.antiquities; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class ClayStatue extends CardImpl { + + public ClayStatue(UUID ownerId) { + super(ownerId, 9, "Clay Statue", Rarity.COMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + this.expansionSetCode = "ATQ"; + this.subtype.add("Golem"); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // {2}: Regenerate Clay Statue. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{2}"))); + } + + public ClayStatue(final ClayStatue card) { + super(card); + } + + @Override + public ClayStatue copy() { + return new ClayStatue(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/apocalypse/FireIce.java b/Mage.Sets/src/mage/sets/apocalypse/FireIce.java index 7f0394cdcd8..18f2d907653 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/FireIce.java +++ b/Mage.Sets/src/mage/sets/apocalypse/FireIce.java @@ -51,12 +51,8 @@ public class FireIce extends SplitCard { super(ownerId, 128, "Fire", "Ice", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{R}", "{1}{U}", false); this.expansionSetCode = "APC"; - this.color.setBlue(true); - this.color.setRed(true); - // Fire // Fire deals 2 damage divided as you choose among one or two target creatures and/or players. - getLeftHalfCard().getColor().setRed(true); Effect effect = new DamageMultiEffect(2); effect.setText("Fire deals 2 damage divided as you choose among one or two target creatures and/or players"); getLeftHalfCard().getSpellAbility().addEffect(effect); @@ -65,7 +61,6 @@ public class FireIce extends SplitCard { // Ice // Tap target permanent. // Draw a card. - getRightHalfCard().getColor().setBlue(true); getRightHalfCard().getSpellAbility().addEffect(new TapTargetEffect()); getRightHalfCard().getSpellAbility().addTarget(new TargetPermanent()); getRightHalfCard().getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); diff --git a/Mage.Sets/src/mage/sets/apocalypse/IllusionReality.java b/Mage.Sets/src/mage/sets/apocalypse/IllusionReality.java index dd3af7ac036..af059572f70 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/IllusionReality.java +++ b/Mage.Sets/src/mage/sets/apocalypse/IllusionReality.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.apocalypse; import java.util.UUID; @@ -50,22 +49,16 @@ public class IllusionReality extends SplitCard { super(ownerId, 129, "Illusion", "Reality", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{U}", "{2}{G}", false); this.expansionSetCode = "APC"; - this.color.setBlue(true); - this.color.setGreen(true); - // Illusion // Target spell or permanent becomes the color of your choice until end of turn. - getLeftHalfCard().getColor().setBlue(true); getLeftHalfCard().getSpellAbility().addEffect(new BecomesColorTargetEffect(Duration.EndOfTurn)); Target target = new TargetSpellOrPermanent(); getLeftHalfCard().getSpellAbility().addTarget(target); // Reality // Destroy target artifact. - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addTarget(new TargetArtifactPermanent()); getRightHalfCard().getSpellAbility().addEffect(new DestroyTargetEffect()); - } public IllusionReality(final IllusionReality card) { diff --git a/Mage.Sets/src/mage/sets/apocalypse/LifeDeath.java b/Mage.Sets/src/mage/sets/apocalypse/LifeDeath.java index 74b76951c09..1db52a16d06 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/LifeDeath.java +++ b/Mage.Sets/src/mage/sets/apocalypse/LifeDeath.java @@ -59,18 +59,13 @@ public class LifeDeath extends SplitCard { super(ownerId, 130, "Life", "Death", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{G}", "{1}{B}", false); this.expansionSetCode = "APC"; - this.color.setGreen(true); - this.color.setBlack(true); - // Life // All lands you control become 1/1 creatures until end of turn. They're still lands. - getLeftHalfCard().getColor().setGreen(true); getLeftHalfCard().getSpellAbility().addEffect(new BecomesCreatureAllEffect(new LifeLandToken(), "lands", new FilterControlledLandPermanent("lands you control"), Duration.EndOfTurn)); // Death // Return target creature card from your graveyard to the battlefield. You lose life equal to its converted mana cost. - getRightHalfCard().getColor().setBlack(true); Target target = new TargetCardInYourGraveyard(1, new FilterCreatureCard("creature card from your graveyard")); getRightHalfCard().getSpellAbility().addTarget(target); getRightHalfCard().getSpellAbility().addEffect(new DeathEffect()); diff --git a/Mage.Sets/src/mage/sets/apocalypse/NightDay.java b/Mage.Sets/src/mage/sets/apocalypse/NightDay.java index 1a29b3bfe59..51889bf52be 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/NightDay.java +++ b/Mage.Sets/src/mage/sets/apocalypse/NightDay.java @@ -59,18 +59,13 @@ public class NightDay extends SplitCard { super(ownerId, 131, "Night", "Day", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{B}", "{2}{W}", false); this.expansionSetCode = "APC"; - this.color.setBlack(true); - this.color.setWhite(true); - // Night // Target creature gets -1/-1 until end of turn. - getLeftHalfCard().getColor().setBlack(true); getLeftHalfCard().getSpellAbility().addEffect(new BoostTargetEffect(-1,-1,Duration.EndOfTurn)); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); // Day // Creatures target player controls get +1/+1 until end of turn. - getRightHalfCard().getColor().setWhite(true); getRightHalfCard().getSpellAbility().addTarget(new TargetPlayer()); getRightHalfCard().getSpellAbility().addEffect(new DayEffect()); diff --git a/Mage.Sets/src/mage/sets/apocalypse/OrderChaos.java b/Mage.Sets/src/mage/sets/apocalypse/OrderChaos.java index af280d76ead..d4e7afbe97e 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/OrderChaos.java +++ b/Mage.Sets/src/mage/sets/apocalypse/OrderChaos.java @@ -51,19 +51,14 @@ public class OrderChaos extends SplitCard { super(ownerId, 132, "Order", "Chaos", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{3}{W}", "{2}{R}", false); this.expansionSetCode = "APC"; - this.color.setWhite(true); - this.color.setRed(true); - // Order // Exile target attacking creature. - getLeftHalfCard().getColor().setWhite(true); getLeftHalfCard().getSpellAbility().addEffect(new ExileTargetEffect()); Target target = new TargetAttackingCreature(); getLeftHalfCard().getSpellAbility().addTarget(target); // Chaos // Creatures can't block this turn. - getRightHalfCard().getColor().setRed(true); getRightHalfCard().getSpellAbility().addEffect(new CantBlockAllEffect(new FilterCreaturePermanent("Creatures"), Duration.EndOfTurn)); } diff --git a/Mage.Sets/src/mage/sets/apocalypse/OrimsThunder.java b/Mage.Sets/src/mage/sets/apocalypse/OrimsThunder.java index 764f0b9a0a0..9300329a73b 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/OrimsThunder.java +++ b/Mage.Sets/src/mage/sets/apocalypse/OrimsThunder.java @@ -28,7 +28,6 @@ package mage.sets.apocalypse; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; @@ -41,9 +40,7 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -55,30 +52,20 @@ import mage.target.common.TargetCreaturePermanent; */ public class OrimsThunder extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public OrimsThunder(UUID ownerId) { super(ownerId, 15, "Orim's Thunder", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{W}"); this.expansionSetCode = "APC"; - // Kicker {R} this.addAbility(new KickerAbility("{R}")); // Destroy target artifact or enchantment. If Orim's Thunder was kicked, it deals damage equal to that permanent's converted mana cost to target creature. this.getSpellAbility().addEffect(new OrimsThunderEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new OrimsThunderEffect2(), KickedCondition.getInstance(), "If Orim's Thunder was kicked, it deals damage equal to that permanent's converted mana cost to target creature")); - } @Override diff --git a/Mage.Sets/src/mage/sets/apocalypse/Smash.java b/Mage.Sets/src/mage/sets/apocalypse/Smash.java index 255670b260a..d744e0f308a 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/Smash.java +++ b/Mage.Sets/src/mage/sets/apocalypse/Smash.java @@ -28,15 +28,12 @@ package mage.sets.apocalypse; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; import mage.target.TargetPermanent; /** @@ -48,7 +45,6 @@ public class Smash extends CardImpl { super(ownerId, 69, "Smash", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{R}"); this.expansionSetCode = "APC"; - // Destroy target artifact. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/ButcherGhoul.java b/Mage.Sets/src/mage/sets/avacynrestored/ButcherGhoul.java index cf186b902ab..4c705d5594b 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/ButcherGhoul.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/ButcherGhoul.java @@ -48,6 +48,7 @@ public class ButcherGhoul extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); + // Undying (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.) this.addAbility(new UndyingAbility()); } diff --git a/Mage.Sets/src/mage/sets/avacynrestored/DevoutChaplain.java b/Mage.Sets/src/mage/sets/avacynrestored/DevoutChaplain.java index a1a951f8103..3b79a31ed8e 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/DevoutChaplain.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/DevoutChaplain.java @@ -78,7 +78,7 @@ public class DevoutChaplain extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // {tap}, Tap two untapped Humans you control: Exile target artifact or enchantment. + // {T}, Tap two untapped Humans you control: Exile target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new TapSourceCost()); ability.addCost(new TapTargetCost(new TargetControlledPermanent(2, 2, humanFilter, false))); ability.addTarget(new TargetPermanent(filter)); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/DreadSlaver.java b/Mage.Sets/src/mage/sets/avacynrestored/DreadSlaver.java index 8dd0ecbfcd2..ff086a81a0f 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/DreadSlaver.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/DreadSlaver.java @@ -138,7 +138,7 @@ class DreadSlaverContiniousEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - creature.getColor().setBlack(true); + creature.getColor(game).setBlack(true); } break; } diff --git a/Mage.Sets/src/mage/sets/avacynrestored/NaturalEnd.java b/Mage.Sets/src/mage/sets/avacynrestored/NaturalEnd.java index 2075204aa25..2f177aa6106 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/NaturalEnd.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/NaturalEnd.java @@ -33,9 +33,7 @@ import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -44,23 +42,14 @@ import mage.target.TargetPermanent; */ public class NaturalEnd extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public NaturalEnd(UUID ownerId) { super(ownerId, 185, "Natural End", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{G}"); this.expansionSetCode = "AVR"; - // Destroy target artifact or enchantment. You gain 3 life. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new GainLifeEffect(3)); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); } public NaturalEnd(final NaturalEnd card) { diff --git a/Mage.Sets/src/mage/sets/avacynrestored/TreacherousPitDweller.java b/Mage.Sets/src/mage/sets/avacynrestored/TreacherousPitDweller.java index 8e5033a0b2d..ac03b230859 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/TreacherousPitDweller.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/TreacherousPitDweller.java @@ -30,7 +30,6 @@ package mage.sets.avacynrestored; import mage.constants.*; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.ZoneChangeTriggeredAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.keyword.UndyingAbility; import mage.cards.CardImpl; @@ -39,6 +38,10 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetOpponent; import java.util.UUID; +import mage.abilities.TriggeredAbilityImpl; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.players.Player; /** * @author noxx @@ -70,30 +73,46 @@ public class TreacherousPitDweller extends CardImpl { } } -class TreacherousPitDwellerTriggeredAbility extends ZoneChangeTriggeredAbility { +class TreacherousPitDwellerTriggeredAbility extends TriggeredAbilityImpl { private static final String ruleText = "When {this} enters the battlefield from a graveyard, "; public TreacherousPitDwellerTriggeredAbility() { - super(Zone.GRAVEYARD, Zone.BATTLEFIELD, new TreacherousPitDwellerEffect(), ruleText, false); + super(Zone.BATTLEFIELD, new TreacherousPitDwellerEffect(),false); addTarget(new TargetOpponent()); } public TreacherousPitDwellerTriggeredAbility(final TreacherousPitDwellerTriggeredAbility ability) { super(ability); } - + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getTargetId().equals(getSourceId()) && ((EntersTheBattlefieldEvent) event).getFromZone().equals(Zone.GRAVEYARD); + } + @Override public TreacherousPitDwellerTriggeredAbility copy() { return new TreacherousPitDwellerTriggeredAbility(this); } + + @Override + public String getRule() { + return ruleText + super.getRule(); + } + } class TreacherousPitDwellerEffect extends ContinuousEffectImpl { public TreacherousPitDwellerEffect() { super(Duration.Custom, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); - staticText = "Target opponent gains control of {this}"; + staticText = "target opponent gains control of {this}"; } public TreacherousPitDwellerEffect(final TreacherousPitDwellerEffect effect) { @@ -108,8 +127,9 @@ class TreacherousPitDwellerEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game); - if (permanent != null) { - return permanent.changeControllerId(source.getFirstTarget(), game); + Player targetOpponent = game.getPlayer(source.getFirstTarget()); + if (permanent != null && targetOpponent != null) { + return permanent.changeControllerId(targetOpponent.getId(), game); } else { discard(); } diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/EmptyShrineKannushi.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/EmptyShrineKannushi.java index 13b8dc0930b..56805bbc971 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/EmptyShrineKannushi.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/EmptyShrineKannushi.java @@ -93,7 +93,7 @@ class EmptyShrineKannushiProtectionAbility extends ProtectionAbility { public boolean canTarget(MageObject source, Game game) { ObjectColor color = new ObjectColor(); for (Permanent permanent: game.getBattlefield().getAllActivePermanents(controllerId)) { - ObjectColor permanentColor = permanent.getColor(); + ObjectColor permanentColor = permanent.getColor(game); if (permanentColor.isColorless()) { continue; } diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/TerashisGrasp.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/TerashisGrasp.java index 355f5ce0c63..92340a737db 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/TerashisGrasp.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/TerashisGrasp.java @@ -28,7 +28,6 @@ package mage.sets.betrayersofkamigawa; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.Ability; @@ -36,9 +35,7 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.Outcome; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -50,22 +47,13 @@ import mage.target.TargetPermanent; */ public class TerashisGrasp extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public TerashisGrasp(UUID ownerId) { super(ownerId, 26, "Terashi's Grasp", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{2}{W}"); this.expansionSetCode = "BOK"; this.subtype.add("Arcane"); - // Destroy target artifact or enchantment. - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new DestroyTargetEffect()); // You gain life equal to its converted mana cost. this.getSpellAbility().addEffect(new TerashisGraspEffect()); diff --git a/Mage.Sets/src/mage/sets/bornofthegods/SatyrFiredancer.java b/Mage.Sets/src/mage/sets/bornofthegods/SatyrFiredancer.java index 7ba3689bbd8..7706bfac8ef 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/SatyrFiredancer.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/SatyrFiredancer.java @@ -36,7 +36,6 @@ import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/AkkiLavarunner.java b/Mage.Sets/src/mage/sets/championsofkamigawa/AkkiLavarunner.java index c30235ab5f1..f0edd96bc58 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/AkkiLavarunner.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/AkkiLavarunner.java @@ -126,7 +126,7 @@ class TokTokVolcanoBornEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { Card card = game.getCard(event.getSourceId()); - if (card != null && card.getColor().isRed()) { + if (card != null && card.getColor(game).isRed()) { return true; } return false; diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java index 251f4c4d820..14109095917 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java @@ -155,7 +155,7 @@ class KondasBannerColorBoostEffect extends BoostAllEffect { if (equipment != null && equipment.getAttachedTo() != null) { Permanent equipedCreature = game.getPermanent(equipment.getAttachedTo()); for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (equipedCreature.getColor().shares(perm.getColor())) { + if (equipedCreature.getColor(game).shares(perm.getColor(game))) { perm.addPower(power.calculate(game, source, this)); perm.addToughness(toughness.calculate(game, source, this)); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/WearAway.java b/Mage.Sets/src/mage/sets/championsofkamigawa/WearAway.java index 0c7c0cf220a..b831a1fa4dd 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/WearAway.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/WearAway.java @@ -33,9 +33,7 @@ import mage.abilities.keyword.SpliceOntoArcaneAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.Target; import mage.target.TargetPermanent; @@ -45,20 +43,14 @@ import mage.target.TargetPermanent; */ public class WearAway extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - static { - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ENCHANTMENT))); - } - public WearAway(UUID ownerId) { super(ownerId, 250, "Wear Away", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G}{G}"); this.expansionSetCode = "CHK"; this.subtype.add("Arcane"); - // Destroy target artifact or enchantment. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - Target target = new TargetPermanent(filter); + Target target = new TargetPermanent(new FilterArtifactOrEnchantmentPermanent()); this.getSpellAbility().addTarget(target); // Splice onto Arcane {3}{G} this.addAbility(new SpliceOntoArcaneAbility("{3}{G}")); diff --git a/Mage.Sets/src/mage/sets/coldsnap/RonomUnicorn.java b/Mage.Sets/src/mage/sets/coldsnap/RonomUnicorn.java new file mode 100644 index 00000000000..879e35fc1d4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/coldsnap/RonomUnicorn.java @@ -0,0 +1,70 @@ +/* + * 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.sets.coldsnap; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.target.TargetPermanent; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class RonomUnicorn extends CardImpl { + + public RonomUnicorn(UUID ownerId) { + super(ownerId, 16, "Ronom Unicorn", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{W}"); + this.expansionSetCode = "CSP"; + this.subtype.add("Unicorn"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Sacrifice Ronom Unicorn: Destroy target enchantment. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost()); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); + this.addAbility(ability); + } + + public RonomUnicorn(final RonomUnicorn card) { + super(card); + } + + @Override + public RonomUnicorn copy() { + return new RonomUnicorn(this); + } +} diff --git a/Mage.Sets/src/mage/sets/commander/AuraShards.java b/Mage.Sets/src/mage/sets/commander/AuraShards.java index c61c4ec75f8..3827c941bb4 100644 --- a/Mage.Sets/src/mage/sets/commander/AuraShards.java +++ b/Mage.Sets/src/mage/sets/commander/AuraShards.java @@ -31,37 +31,26 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.target.TargetPermanent; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.target.TargetPermanent; /** * * @author emerald000 */ public class AuraShards extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public AuraShards(UUID ownerId) { super(ownerId, 182, "Aura Shards", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}{W}"); this.expansionSetCode = "CMD"; - // Whenever a creature enters the battlefield under your control, you may destroy target artifact or enchantment. Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new FilterCreaturePermanent(), true, "Whenever a creature enters the battlefield under your control, you may destroy target artifact or enchantment"); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/commander/SewerNemesis.java b/Mage.Sets/src/mage/sets/commander/SewerNemesis.java index 5942953b0c1..ee6da4ab795 100644 --- a/Mage.Sets/src/mage/sets/commander/SewerNemesis.java +++ b/Mage.Sets/src/mage/sets/commander/SewerNemesis.java @@ -102,11 +102,11 @@ class SewerNemesisChoosePlayerEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getSourceId()); if (player != null && permanent != null) { - TargetPlayer target = new TargetPlayer(); + TargetPlayer target = new TargetPlayer(1,1,true); if (player.choose(this.outcome, target, source.getSourceId(), game)) { Player chosenPlayer = game.getPlayer(target.getFirstTarget()); if (chosenPlayer != null) { - game.informPlayers(permanent.getName() + ": " + player.getLogName() + " has chosen " + chosenPlayer.getLogName()); + game.informPlayers(permanent.getLogName() + ": " + player.getLogName() + " has chosen " + chosenPlayer.getLogName()); game.getState().setValue(permanent.getId() + "_player", target.getFirstTarget()); return true; } diff --git a/Mage.Sets/src/mage/sets/commander2013/OpalPalace.java b/Mage.Sets/src/mage/sets/commander2013/OpalPalace.java index 33704e9e315..4601e1a29d5 100644 --- a/Mage.Sets/src/mage/sets/commander2013/OpalPalace.java +++ b/Mage.Sets/src/mage/sets/commander2013/OpalPalace.java @@ -64,12 +64,12 @@ public class OpalPalace extends CardImpl { super(ownerId, 310, "Opal Palace", Rarity.COMMON, new CardType[]{CardType.LAND}, ""); this.expansionSetCode = "C13"; - // {tap}: Add {1} to your mana pool. + // {T}: Add {1} to your mana pool. this.addAbility(new ColorlessManaAbility()); // {1}, {tap}: Add to your mana pool one mana of any color in your commander's color identity. If you spend this mana to cast your commander, it enters the battlefield with a number of +1/+1 counters on it equal to the number of times it's been cast from the command zone this game. Ability ability = new CommanderColorIdentityManaAbility(new GenericManaCost(1)); ability.addCost(new TapSourceCost()); - this.addAbility(ability, new OpalPalaceWatcher()); + this.addAbility(ability, new OpalPalaceWatcher(ability.getOriginalId().toString())); ability = new SimpleStaticAbility(Zone.ALL, new OpalPalaceEntersBattlefieldEffect()); ability.setRuleVisible(false); @@ -90,13 +90,17 @@ public class OpalPalace extends CardImpl { class OpalPalaceWatcher extends Watcher { public List commanderId = new ArrayList<>(); - - public OpalPalaceWatcher() { + private final String originalId; + + public OpalPalaceWatcher(String originalId) { super("ManaPaidFromOpalPalaceWatcher", WatcherScope.CARD); + this.originalId = originalId; } public OpalPalaceWatcher(final OpalPalaceWatcher watcher) { super(watcher); + this.commanderId.addAll(watcher.commanderId); + this.originalId = watcher.originalId; } @Override @@ -107,7 +111,7 @@ class OpalPalaceWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.MANA_PAYED) { - if (event.getSourceId().equals(this.getSourceId()) && event.getFlag()) { // flag indicates that mana was produced with second ability + if (event.getData() != null && event.getData().equals(originalId)) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null) { Card card = spell.getCard(); diff --git a/Mage.Sets/src/mage/sets/commander2013/RoughTumble.java b/Mage.Sets/src/mage/sets/commander2013/RoughTumble.java index e281caac892..14ec4d4e8be 100644 --- a/Mage.Sets/src/mage/sets/commander2013/RoughTumble.java +++ b/Mage.Sets/src/mage/sets/commander2013/RoughTumble.java @@ -55,20 +55,16 @@ public class RoughTumble extends SplitCard { super(ownerId, 118, "Rough", "Tumble", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{1}{R}", "{5}{R}", false); this.expansionSetCode = "C13"; - this.color.setRed(true); - // Rough // Rough deals 2 damage to each creature without flying. Effect effect = new DamageAllEffect(2, filterWithoutFlying); effect.setText("Rough deals 2 damage to each creature without flying"); - getLeftHalfCard().getColor().setRed(true); getLeftHalfCard().getSpellAbility().addEffect(effect); // Tumble // Tumble deals 6 damage to each creature with flying. effect = new DamageAllEffect(6, filterFlying); effect.setText("Tumble deals 6 damage to each creature with flying"); - getRightHalfCard().getColor().setRed(true); getRightHalfCard().getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/sets/commander2014/FreyaliseLlanowarsFury.java b/Mage.Sets/src/mage/sets/commander2014/FreyaliseLlanowarsFury.java index 9a85986a5f4..adf5daa6ca5 100644 --- a/Mage.Sets/src/mage/sets/commander2014/FreyaliseLlanowarsFury.java +++ b/Mage.Sets/src/mage/sets/commander2014/FreyaliseLlanowarsFury.java @@ -43,10 +43,8 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.counters.CounterType; -import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.permanent.token.Token; import mage.target.TargetPermanent; @@ -57,13 +55,8 @@ import mage.target.TargetPermanent; */ public class FreyaliseLlanowarsFury extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); private static final FilterControlledCreaturePermanent filterGreen = new FilterControlledCreaturePermanent("green creature you control"); - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); filterGreen.add(new ColorPredicate(ObjectColor.GREEN)); } @@ -72,21 +65,19 @@ public class FreyaliseLlanowarsFury extends CardImpl { this.expansionSetCode = "C14"; this.subtype.add("Freyalise"); - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(3)), false)); // +2: Put a 1/1 green Elf Druid creature token onto the battlefield with "{T}: Add {G} to your mana pool." this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new FreyaliseLlanowarsFuryToken()), 2)); // -2: Destroy target artifact or enchantment. LoyaltyAbility loyaltyAbility = new LoyaltyAbility(new DestroyTargetEffect(), -2); - loyaltyAbility.addTarget(new TargetPermanent(filter)); + loyaltyAbility.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(loyaltyAbility); // -6: Draw a card for each green creature you control. this.addAbility(new LoyaltyAbility(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(filterGreen)), -6)); // Freyalise, Llanowar's Fury can be your commander. this.addAbility(CanBeYourCommanderAbility.getInstance()); - } public FreyaliseLlanowarsFury(final FreyaliseLlanowarsFury card) { diff --git a/Mage.Sets/src/mage/sets/commander2014/NecromanticSelection.java b/Mage.Sets/src/mage/sets/commander2014/NecromanticSelection.java index 4877ed2151b..b595331cf91 100644 --- a/Mage.Sets/src/mage/sets/commander2014/NecromanticSelection.java +++ b/Mage.Sets/src/mage/sets/commander2014/NecromanticSelection.java @@ -168,7 +168,7 @@ class NecromanticSelectionContinuousEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - creature.getColor().setBlack(true); + creature.getColor(game).setBlack(true); } break; } diff --git a/Mage.Sets/src/mage/sets/commander2014/WaveOfVitriol.java b/Mage.Sets/src/mage/sets/commander2014/WaveOfVitriol.java index de2c61fe28c..bf1405c76a0 100644 --- a/Mage.Sets/src/mage/sets/commander2014/WaveOfVitriol.java +++ b/Mage.Sets/src/mage/sets/commander2014/WaveOfVitriol.java @@ -27,6 +27,8 @@ */ package mage.sets.commander2014; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -57,7 +59,6 @@ public class WaveOfVitriol extends CardImpl { super(ownerId, 51, "Wave of Vitriol", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{5}{G}{G}"); this.expansionSetCode = "C14"; - // Each player sacrifices all artifacts, enchantments, and nonbasic lands he or she controls. For each land sacrificed this way, its controller may search his or her library for a basic land card and put it onto the battlefield tapped. Then each player who searched his or her library this way shuffles it. this.getSpellAbility().addEffect(new WaveOfVitriolEffect()); @@ -106,6 +107,7 @@ class WaveOfVitriolEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { + Map sacrificedLands = new HashMap<>(); for(UUID playerId: controller.getInRange()) { Player player = game.getPlayer(playerId); if (player != null) { @@ -115,21 +117,33 @@ class WaveOfVitriolEffect extends OneShotEffect { count++; } } - game.getState().handleSimultaneousEvent(game); - if (count > 0 && player.chooseUse(Outcome.PutLandInPlay, "Search your library for up to " + count + " basic lands?", game)) { - Target target = new TargetCardInLibrary(0,count, new FilterBasicLandCard()); - player.chooseTarget(outcome, target, source, game); - for(UUID targetId: target.getTargets()) { - Card card = game.getCard(targetId); - if (card != null) { - player.putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, source.getSourceId(), true); - } - } - player.shuffleLibrary(game); - game.getState().handleSimultaneousEvent(game); + if (count > 0) { + sacrificedLands.put(player, count); } } } + game.getState().handleSimultaneousEvent(game); + for(Map.Entry entry: sacrificedLands.entrySet()) { + if (entry.getKey().chooseUse(Outcome.PutLandInPlay, "Search your library for up to " + entry.getValue() + " basic lands?", game)) { + Target target = new TargetCardInLibrary(0, entry.getValue(), new FilterBasicLandCard()); + entry.getKey().chooseTarget(outcome, target, source, game); + for(UUID targetId: target.getTargets()) { + Card card = game.getCard(targetId); + if (card != null) { + entry.getKey().putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, source.getSourceId(), true); + } + } + entry.getKey().shuffleLibrary(game); + } else { + entry.setValue(0); + } + } + for(Map.Entry entry: sacrificedLands.entrySet()) { + if (entry.getValue() > 0) { + entry.getKey().shuffleLibrary(game); + } + } + return true; } return false; diff --git a/Mage.Sets/src/mage/sets/conflux/FiligreeFracture.java b/Mage.Sets/src/mage/sets/conflux/FiligreeFracture.java index e229e08cb0f..1e6b0bb23a1 100644 --- a/Mage.Sets/src/mage/sets/conflux/FiligreeFracture.java +++ b/Mage.Sets/src/mage/sets/conflux/FiligreeFracture.java @@ -36,9 +36,7 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -50,21 +48,12 @@ import mage.target.TargetPermanent; */ public class FiligreeFracture extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public FiligreeFracture(UUID ownerId) { super(ownerId, 82, "Filigree Fracture", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{G}"); this.expansionSetCode = "CON"; - // Destroy target artifact or enchantment. If that permanent was blue or black, draw a card. - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new FiligreeFractureEffect()); } @@ -100,7 +89,7 @@ class FiligreeFractureEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); Permanent permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); if (player != null && permanent != null - && (permanent.getColor().isBlack() || permanent.getColor().isBlue())) { + && (permanent.getColor(game).isBlack() || permanent.getColor(game).isBlue())) { player.drawCards(1, game); return true; } diff --git a/Mage.Sets/src/mage/sets/conflux/KederektParasite.java b/Mage.Sets/src/mage/sets/conflux/KederektParasite.java index bbe0731e8be..cf42f9ab4af 100644 --- a/Mage.Sets/src/mage/sets/conflux/KederektParasite.java +++ b/Mage.Sets/src/mage/sets/conflux/KederektParasite.java @@ -89,7 +89,7 @@ class KederektParasiteTriggeredAbility extends TriggeredAbilityImpl { if (event.getType() == GameEvent.EventType.DREW_CARD && game.getOpponents(this.getControllerId()).contains(event.getPlayerId())) { boolean youControlRedPermanent = false; for (Permanent permanent : game.getBattlefield().getAllActivePermanents(this.getControllerId())) { - if (permanent.getColor().isRed()) { + if (permanent.getColor(game).isRed()) { youControlRedPermanent = true; break; } diff --git a/Mage.Sets/src/mage/sets/conflux/SphinxSummoner.java b/Mage.Sets/src/mage/sets/conflux/SphinxSummoner.java index aa6532ea9fa..ca99e4bcb24 100644 --- a/Mage.Sets/src/mage/sets/conflux/SphinxSummoner.java +++ b/Mage.Sets/src/mage/sets/conflux/SphinxSummoner.java @@ -45,6 +45,7 @@ import mage.target.common.TargetCardInLibrary; * @author Loki */ public class SphinxSummoner extends CardImpl { + private static final FilterCard filter = new FilterCard("artifact creature card"); static { @@ -62,7 +63,7 @@ public class SphinxSummoner extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Sphinx Summoner enters the battlefield, you may search your library for an artifact creature card, reveal it, and put it into your hand. If you do, shuffle your library. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter)), true)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, false), true)); } public SphinxSummoner(final SphinxSummoner card) { diff --git a/Mage.Sets/src/mage/sets/conspiracy/DackFayden.java b/Mage.Sets/src/mage/sets/conspiracy/DackFayden.java index 98a7b8650a0..0ad44a2f54a 100644 --- a/Mage.Sets/src/mage/sets/conspiracy/DackFayden.java +++ b/Mage.Sets/src/mage/sets/conspiracy/DackFayden.java @@ -84,7 +84,7 @@ public class DackFayden extends CardImpl { this.addAbility(ability); // -2: Gain control of target artifact. - effect = new GainControlTargetEffect(Duration.EndOfGame); + effect = new GainControlTargetEffect(Duration.EndOfGame, true); effect.setText("Gain control of target artifact"); ability = new LoyaltyAbility(effect, -2); ability.addTarget(new TargetArtifactPermanent()); diff --git a/Mage.Sets/src/mage/sets/conspiracy/Victimize.java b/Mage.Sets/src/mage/sets/conspiracy/Victimize.java index fe071b297d4..5f8a7bf3fc4 100644 --- a/Mage.Sets/src/mage/sets/conspiracy/Victimize.java +++ b/Mage.Sets/src/mage/sets/conspiracy/Victimize.java @@ -54,7 +54,6 @@ public class Victimize extends CardImpl { super(ownerId, 133, "Victimize", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2}{B}"); this.expansionSetCode = "CNS"; - // Choose two target creature cards in your graveyard. Sacrifice a creature. If you do, return the chosen cards to the battlefield tapped. this.getSpellAbility().addEffect(new VictimizeEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(2, new FilterCreatureCard("creature cards in your graveyard"))); @@ -88,14 +87,14 @@ class VictimizeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { SacrificeTargetCost cost = new SacrificeTargetCost(new TargetControlledCreaturePermanent(new FilterControlledCreaturePermanent("a creature"))); - if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), true)) { + if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { for (UUID targetId: getTargetPointer().getTargets(game, source)) { Card card = game.getCard(targetId); if (card != null) { - player.putOntoBattlefieldWithInfo(card, game, Zone.GRAVEYARD, source.getSourceId(), true); + controller.putOntoBattlefieldWithInfo(card, game, Zone.GRAVEYARD, source.getSourceId(), true); } } } diff --git a/Mage.Sets/src/mage/sets/darkascension/TorchFiend.java b/Mage.Sets/src/mage/sets/darkascension/TorchFiend.java index 662c0104c1f..285ab2610f7 100644 --- a/Mage.Sets/src/mage/sets/darkascension/TorchFiend.java +++ b/Mage.Sets/src/mage/sets/darkascension/TorchFiend.java @@ -28,7 +28,6 @@ package mage.sets.darkascension; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; diff --git a/Mage.Sets/src/mage/sets/darkascension/WerewolfRansacker.java b/Mage.Sets/src/mage/sets/darkascension/WerewolfRansacker.java index 452b74d0faa..20b2619b740 100644 --- a/Mage.Sets/src/mage/sets/darkascension/WerewolfRansacker.java +++ b/Mage.Sets/src/mage/sets/darkascension/WerewolfRansacker.java @@ -28,7 +28,6 @@ package mage.sets.darkascension; import java.util.UUID; - import mage.constants.*; import mage.MageInt; import mage.abilities.Ability; @@ -45,6 +44,7 @@ import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -90,17 +90,12 @@ public class WerewolfRansacker extends CardImpl { } class WerewolfRansackerAbility extends TriggeredAbilityImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact"); + public static final String RULE_TEXT = "Whenever this creature transforms into Werewolf Ransacker, you may destroy target artifact. If that artifact is put into a graveyard this way, Werewolf Ransacker deals 3 damage to that artifact's controller"; - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - + public WerewolfRansackerAbility() { super(Zone.BATTLEFIELD, new WerewolfRansackerEffect(), true); - Target target = new TargetPermanent(filter); + Target target = new TargetPermanent(new FilterArtifactPermanent()); this.addTarget(target); } @@ -129,7 +124,6 @@ class WerewolfRansackerAbility extends TriggeredAbilityImpl { public String getRule() { return RULE_TEXT + '.'; } - } class WerewolfRansackerEffect extends OneShotEffect { @@ -167,5 +161,4 @@ class WerewolfRansackerEffect extends OneShotEffect { } return affectedTargets > 0; } - } diff --git a/Mage.Sets/src/mage/sets/darksteel/BlinkmothNexus.java b/Mage.Sets/src/mage/sets/darksteel/BlinkmothNexus.java index 42204e235e8..2ddadf0ed3a 100644 --- a/Mage.Sets/src/mage/sets/darksteel/BlinkmothNexus.java +++ b/Mage.Sets/src/mage/sets/darksteel/BlinkmothNexus.java @@ -63,8 +63,14 @@ public class BlinkmothNexus extends CardImpl { public BlinkmothNexus(UUID ownerId) { super(ownerId, 163, "Blinkmoth Nexus", Rarity.RARE, new CardType[]{CardType.LAND}, null); this.expansionSetCode = "DST"; + + // {T}: Add {1}to your mana pool. this.addAbility(new ColorlessManaAbility()); + + // {1}: Blinkmoth Nexus becomes a 1/1 Blinkmoth artifact creature with flying until end of turn. It's still a land. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new BlinkmothNexusToken(), "land", Duration.EndOfTurn), new GenericManaCost(1))); + + // {1}, {T}: Target Blinkmoth creature gets +1/+1 until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(1, 1, Duration.EndOfTurn), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetPermanent(filter)); diff --git a/Mage.Sets/src/mage/sets/darksteel/DarksteelCitadel.java b/Mage.Sets/src/mage/sets/darksteel/DarksteelCitadel.java index 8fab79820b4..d230e02a283 100644 --- a/Mage.Sets/src/mage/sets/darksteel/DarksteelCitadel.java +++ b/Mage.Sets/src/mage/sets/darksteel/DarksteelCitadel.java @@ -44,7 +44,11 @@ public class DarksteelCitadel extends CardImpl { public DarksteelCitadel (UUID ownerId) { super(ownerId, 164, "Darksteel Citadel", Rarity.COMMON, new CardType[]{CardType.ARTIFACT, CardType.LAND}, null); this.expansionSetCode = "DST"; + + // Indestructible (Effects that say "destroy" don't destroy this land.) this.addAbility(IndestructibleAbility.getInstance()); + + // {T}: Add {1} to your mana pool. this.addAbility(new ColorlessManaAbility()); } diff --git a/Mage.Sets/src/mage/sets/darksteel/MycosynthLattice.java b/Mage.Sets/src/mage/sets/darksteel/MycosynthLattice.java index f45b2cc3f64..1cd753fd6d0 100644 --- a/Mage.Sets/src/mage/sets/darksteel/MycosynthLattice.java +++ b/Mage.Sets/src/mage/sets/darksteel/MycosynthLattice.java @@ -121,36 +121,36 @@ class EverythingIsColorlessEffect extends ContinuousEffectImpl { ObjectColor colorless = new ObjectColor(); // permaments for (Permanent perm : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - perm.getColor().setColor(colorless); + perm.getColor(game).setColor(colorless); } // spells for (MageObject object : game.getStack()) { if (object instanceof Spell) { - object.getColor().setColor(colorless); + object.getColor(game).setColor(colorless); } } // exile for (Card card : game.getExile().getAllCards(game)) { - card.getColor().setColor(colorless); + game.getState().getCreateCardAttribute(card).getColor().setColor(colorless); } // command for (CommandObject commandObject : game.getState().getCommand()) { - commandObject.getColor().setColor(colorless); + commandObject.getColor(game).setColor(colorless); } for (UUID playerId : controller.getInRange()) { Player player = game.getPlayer(playerId); if (player != null) { // hand for (Card card: player.getHand().getCards(game)) { - card.getColor().setColor(colorless); + game.getState().getCreateCardAttribute(card).getColor().setColor(colorless); } // library for (Card card : player.getLibrary().getCards(game)) { - card.getColor().setColor(colorless); + game.getState().getCreateCardAttribute(card).getColor().setColor(colorless); } // graveyard for (Card card : player.getGraveyard().getCards(game)) { - card.getColor().setColor(colorless); + game.getState().getCreateCardAttribute(card).getColor().setColor(colorless); } } } diff --git a/Mage.Sets/src/mage/sets/darksteel/Oxidize.java b/Mage.Sets/src/mage/sets/darksteel/Oxidize.java index a0d075f0923..0cae108e86f 100644 --- a/Mage.Sets/src/mage/sets/darksteel/Oxidize.java +++ b/Mage.Sets/src/mage/sets/darksteel/Oxidize.java @@ -25,17 +25,14 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.darksteel; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -43,18 +40,12 @@ import mage.target.TargetPermanent; */ public class Oxidize extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Oxidize(UUID ownerId) { super(ownerId, 79, "Oxidize", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{G}"); this.expansionSetCode = "DST"; this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); } public Oxidize(final Oxidize card) { @@ -65,5 +56,4 @@ public class Oxidize extends CardImpl { public Oxidize copy() { return new Oxidize(this); } - } diff --git a/Mage.Sets/src/mage/sets/darksteel/PulseOfTheFields.java b/Mage.Sets/src/mage/sets/darksteel/PulseOfTheFields.java new file mode 100644 index 00000000000..49191523e34 --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/PulseOfTheFields.java @@ -0,0 +1,99 @@ +/* + * 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.sets.darksteel; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author emerald000 + */ +public class PulseOfTheFields extends CardImpl { + + public PulseOfTheFields(UUID ownerId) { + super(ownerId, 11, "Pulse of the Fields", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{1}{W}{W}"); + this.expansionSetCode = "DST"; + + // You gain 4 life. Then if an opponent has more life than you, return Pulse of the Fields to its owner's hand. + this.getSpellAbility().addEffect(new GainLifeEffect(4)); + this.getSpellAbility().addEffect(new PulseOfTheFieldsReturnToHandEffect()); + } + + public PulseOfTheFields(final PulseOfTheFields card) { + super(card); + } + + @Override + public PulseOfTheFields copy() { + return new PulseOfTheFields(this); + } +} + +class PulseOfTheFieldsReturnToHandEffect extends OneShotEffect { + + PulseOfTheFieldsReturnToHandEffect() { + super(Outcome.Benefit); + this.staticText = "Then if an opponent has more life than you, return {this} to its owner's hand"; + } + + PulseOfTheFieldsReturnToHandEffect(final PulseOfTheFieldsReturnToHandEffect effect) { + super(effect); + } + + @Override + public PulseOfTheFieldsReturnToHandEffect copy() { + return new PulseOfTheFieldsReturnToHandEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId : controller.getInRange()) { + Player player = game.getPlayer(playerId); + if (player != null && player.getLife() > controller.getLife()) { + Card card = game.getCard(source.getSourceId()); + controller.moveCardToHandWithInfo(card, source.getSourceId(), game, Zone.STACK); + return true; + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/darksteel/ViridianZealot.java b/Mage.Sets/src/mage/sets/darksteel/ViridianZealot.java index ba3372745f9..d8e1ace7d80 100644 --- a/Mage.Sets/src/mage/sets/darksteel/ViridianZealot.java +++ b/Mage.Sets/src/mage/sets/darksteel/ViridianZealot.java @@ -25,11 +25,9 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.darksteel; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; @@ -40,9 +38,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -50,14 +46,6 @@ import mage.target.TargetPermanent; */ public class ViridianZealot extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public ViridianZealot(UUID ownerId) { super(ownerId, 90, "Viridian Zealot", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{G}{G}"); this.expansionSetCode = "DST"; @@ -66,9 +54,11 @@ public class ViridianZealot extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); + + // {1}{G}, Sacrifice Viridian Zealot: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{1}{G}")); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } @@ -80,5 +70,4 @@ public class ViridianZealot extends CardImpl { public ViridianZealot copy() { return new ViridianZealot(this); } - } diff --git a/Mage.Sets/src/mage/sets/dissension/HideSeek.java b/Mage.Sets/src/mage/sets/dissension/HideSeek.java index 6d407fea200..ec7a7158069 100644 --- a/Mage.Sets/src/mage/sets/dissension/HideSeek.java +++ b/Mage.Sets/src/mage/sets/dissension/HideSeek.java @@ -65,21 +65,13 @@ public class HideSeek extends SplitCard { super(ownerId, 151, "Hide", "Seek", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{R}{W}", "{W}{B}", false); this.expansionSetCode = "DIS"; - this.color.setRed(true); - this.color.setWhite(true); - this.color.setBlack(true); - // Hide // Put target artifact or enchantment on the bottom of its owner's library. - getLeftHalfCard().getColor().setRed(true); - getLeftHalfCard().getColor().setWhite(true); getLeftHalfCard().getSpellAbility().addEffect(new PutOnLibraryTargetEffect(false)); getLeftHalfCard().getSpellAbility().addTarget(new TargetPermanent(filter)); // Seek // Search target opponent's library for a card and exile it. You gain life equal to its converted mana cost. Then that player shuffles his or her library.. - getRightHalfCard().getColor().setWhite(true); - getRightHalfCard().getColor().setBlack(true); getRightHalfCard().getSpellAbility().addEffect(new SeekEffect()); getRightHalfCard().getSpellAbility().addTarget(new TargetOpponent()); diff --git a/Mage.Sets/src/mage/sets/dissension/IndrikStomphowler.java b/Mage.Sets/src/mage/sets/dissension/IndrikStomphowler.java index ab3cfcce029..0181c37f9e2 100644 --- a/Mage.Sets/src/mage/sets/dissension/IndrikStomphowler.java +++ b/Mage.Sets/src/mage/sets/dissension/IndrikStomphowler.java @@ -28,7 +28,6 @@ package mage.sets.dissension; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; @@ -36,9 +35,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -46,14 +43,6 @@ import mage.target.TargetPermanent; */ public class IndrikStomphowler extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public IndrikStomphowler(UUID ownerId) { super(ownerId, 86, "Indrik Stomphowler", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{4}{G}"); this.expansionSetCode = "DIS"; @@ -62,7 +51,7 @@ public class IndrikStomphowler extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/dissension/OddsEnds.java b/Mage.Sets/src/mage/sets/dissension/OddsEnds.java index 761f8a793df..c30478d4cc9 100644 --- a/Mage.Sets/src/mage/sets/dissension/OddsEnds.java +++ b/Mage.Sets/src/mage/sets/dissension/OddsEnds.java @@ -65,21 +65,13 @@ public class OddsEnds extends SplitCard { super(ownerId, 153, "Odds", "Ends", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{U}{R}", "{3}{R}{W}", false); this.expansionSetCode = "DIS"; - this.color.setBlue(true); - this.color.setRed(true); - this.color.setWhite(true); - // Odds // Flip a coin. If it comes up heads, counter target instant or sorcery spell. If it comes up tails, copy that spell and you may choose new targets for the copy. - getLeftHalfCard().getColor().setBlue(true); - getLeftHalfCard().getColor().setRed(true); getLeftHalfCard().getSpellAbility().addEffect(new OddsEffect()); getLeftHalfCard().getSpellAbility().addTarget(new TargetSpell(filter)); // Ends // Target player sacrifices two attacking creatures. - getRightHalfCard().getColor().setRed(true); - getRightHalfCard().getColor().setWhite(true); getRightHalfCard().getSpellAbility().addEffect(new SacrificeEffect(new FilterAttackingCreature(), 2, "Target player")); getRightHalfCard().getSpellAbility().addTarget(new TargetPlayer()); diff --git a/Mage.Sets/src/mage/sets/dissension/PillarOfTheParuns.java b/Mage.Sets/src/mage/sets/dissension/PillarOfTheParuns.java index c1be32d897e..8278800e994 100644 --- a/Mage.Sets/src/mage/sets/dissension/PillarOfTheParuns.java +++ b/Mage.Sets/src/mage/sets/dissension/PillarOfTheParuns.java @@ -96,7 +96,7 @@ class MultiColoredSpellCastManaCondition extends ManaCondition implements Condit public boolean apply(Game game, Ability source) { if (source instanceof SpellAbility) { MageObject object = game.getObject(source.getSourceId()); - if (object != null && object.getColor().getColorCount() > 1) { + if (object != null && object.getColor(game).getColorCount() > 1) { return true; } } diff --git a/Mage.Sets/src/mage/sets/dissension/ResearchDevelopment.java b/Mage.Sets/src/mage/sets/dissension/ResearchDevelopment.java index 6862047730f..353b431a555 100644 --- a/Mage.Sets/src/mage/sets/dissension/ResearchDevelopment.java +++ b/Mage.Sets/src/mage/sets/dissension/ResearchDevelopment.java @@ -57,18 +57,10 @@ public class ResearchDevelopment extends SplitCard { super(ownerId, 155, "Research", "Development", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{G}{U}", "{3}{U}{R}", false); this.expansionSetCode = "DIS"; - this.color.setGreen(true); - this.color.setBlue(true); - this.color.setRed(true); - // Choose up to four cards you own from outside the game and shuffle them into your library. - getLeftHalfCard().getColor().setGreen(true); - getLeftHalfCard().getColor().setBlue(true); getLeftHalfCard().getSpellAbility().addEffect(new ResearchEffect()); // Put a 3/1 red Elemental creature token onto the battlefield unless any opponent has you draw a card. Repeat this process two more times. - getRightHalfCard().getColor().setBlue(true); - getRightHalfCard().getColor().setRed(true); getRightHalfCard().getSpellAbility().addEffect(new DevelopmentEffect()); } diff --git a/Mage.Sets/src/mage/sets/dissension/SupplyDemand.java b/Mage.Sets/src/mage/sets/dissension/SupplyDemand.java index db0edc63172..8eae6bf7da0 100644 --- a/Mage.Sets/src/mage/sets/dissension/SupplyDemand.java +++ b/Mage.Sets/src/mage/sets/dissension/SupplyDemand.java @@ -56,20 +56,12 @@ public class SupplyDemand extends SplitCard { super(ownerId, 157, "Supply", "Demand", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{1}{W}{U}", "{X}{G}{W}", false); this.expansionSetCode = "DIS"; - this.color.setGreen(true); - this.color.setWhite(true); - this.color.setBlue(true); - // Demand // Search your library for a multicolored card, reveal it, and put it into your hand. Then shuffle your library. - getLeftHalfCard().getColor().setBlue(true); - getLeftHalfCard().getColor().setWhite(true); getLeftHalfCard().getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(1, filter), true)); // Supply // Put X 1/1 green Saproling creature tokens onto the battlefield. - getRightHalfCard().getColor().setWhite(true); - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), new ManacostVariableValue())); } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/AliveWell.java b/Mage.Sets/src/mage/sets/dragonsmaze/AliveWell.java index e7c5a1be8a6..7c327b0b424 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/AliveWell.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/AliveWell.java @@ -50,18 +50,13 @@ public class AliveWell extends SplitCard { super(ownerId, 121, "Alive", "Well", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{G}","{W}", true); this.expansionSetCode = "DGM"; - this.color.setGreen(true); - this.color.setWhite(true); - // Alive // Put a 3/3 green Centaur creature token onto the battlefield. - getLeftHalfCard().getColor().setGreen(true); getLeftHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new CentaurToken())); // Well // You gain 2 life for each creature you control. - getRightHalfCard().getColor().setWhite(true); getRightHalfCard().getSpellAbility().addEffect(new WellEffect()); } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ArmedDangerous.java b/Mage.Sets/src/mage/sets/dragonsmaze/ArmedDangerous.java index b72294c0f8b..9e254100237 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ArmedDangerous.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ArmedDangerous.java @@ -51,19 +51,14 @@ public class ArmedDangerous extends SplitCard { super(ownerId, 122, "Armed", "Dangerous", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{1}{R}", "{3}{G}", true); this.expansionSetCode = "DGM"; - this.color.setRed(true); - this.color.setGreen(true); - // Armed // Target creature gets +1/+1 and gains double strike until end of turn. - getLeftHalfCard().getColor().setRed(true); getLeftHalfCard().getSpellAbility().addEffect(new BoostTargetEffect(1,1, Duration.EndOfTurn)); getLeftHalfCard().getSpellAbility().addEffect(new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn)); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); // Dangerous // All creatures able to block target creature this turn do so. - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addEffect(new MustBeBlockedByAllTargetEffect(Duration.EndOfTurn)); getRightHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/BeckCall.java b/Mage.Sets/src/mage/sets/dragonsmaze/BeckCall.java index 5aa0750d09b..ad25ec436b2 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/BeckCall.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/BeckCall.java @@ -55,20 +55,12 @@ public class BeckCall extends SplitCard { super(ownerId, 123, "Beck", "Call", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{G}{U}", "{4}{W}{U}", true); this.expansionSetCode = "DGM"; - this.color.setWhite(true); - this.color.setBlue(true); - this.color.setGreen(true); - // Beck // Whenever a creature enters the battlefield this turn, you may draw a card. - getLeftHalfCard().getColor().setGreen(true); - getLeftHalfCard().getColor().setBlue(true); getLeftHalfCard().getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new BeckTriggeredAbility())); // Call // Put four 1/1 white Bird creature tokens with flying onto the battlefield. - getRightHalfCard().getColor().setWhite(true); - getRightHalfCard().getColor().setBlue(true); getRightHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new BirdToken(),4)); } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/DownDirty.java b/Mage.Sets/src/mage/sets/dragonsmaze/DownDirty.java index 8f6e6827ec5..9619feceec8 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/DownDirty.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/DownDirty.java @@ -49,18 +49,13 @@ public class DownDirty extends SplitCard { super(ownerId, 126, "Down", "Dirty", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{B}", "{2}{G}", true); this.expansionSetCode = "DGM"; - this.color.setBlack(true); - this.color.setGreen(true); - // Down // Target player discards two cards. - getLeftHalfCard().getColor().setBlack(true); getLeftHalfCard().getSpellAbility().addEffect(new DiscardTargetEffect(2)); getLeftHalfCard().getSpellAbility().addTarget(new TargetPlayer()); // Dirty // Return target card from your graveyard to your hand. - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); getRightHalfCard().getSpellAbility().addTarget(new TargetCardInYourGraveyard()); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/FarAway.java b/Mage.Sets/src/mage/sets/dragonsmaze/FarAway.java index a35e569b005..1deba123e03 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/FarAway.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/FarAway.java @@ -50,18 +50,13 @@ public class FarAway extends SplitCard { super(ownerId, 127, "Far", "Away", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{U}", "{2}{B}", true); this.expansionSetCode = "DGM"; - this.color.setBlue(true); - this.color.setBlack(true); - // Far // Return target creature to its owner's hand. - getLeftHalfCard().getColor().setBlue(true); getLeftHalfCard().getSpellAbility().addEffect(new ReturnToHandTargetEffect()); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); // Away // Target player sacrifices a creature. - getRightHalfCard().getColor().setBlack(true); getRightHalfCard().getSpellAbility().addEffect(new SacrificeEffect(new FilterCreaturePermanent(), 1, "Target player")); getRightHalfCard().getSpellAbility().addTarget(new TargetPlayer()); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/FleshBlood.java b/Mage.Sets/src/mage/sets/dragonsmaze/FleshBlood.java index daf7132e91e..547266d6b68 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/FleshBlood.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/FleshBlood.java @@ -60,14 +60,8 @@ public class FleshBlood extends SplitCard { super(ownerId, 128, "Flesh", "Blood", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{3}{B}{G}", "{R}{G}",true); this.expansionSetCode = "DGM"; - this.color.setBlack(true); - this.color.setGreen(true); - this.color.setRed(true); - // Flesh // Exile target creature card from a graveyard. Put X +1/+1 counters on target creature, where X is the power of the card you exiled. - getLeftHalfCard().getColor().setBlack(true); - getLeftHalfCard().getColor().setGreen(true); Target target = new TargetCardInGraveyard(new FilterCreatureCard()); getLeftHalfCard().getSpellAbility().addTarget(target); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); @@ -75,8 +69,6 @@ public class FleshBlood extends SplitCard { // Blood // Target creature you control deals damage equal to its power to target creature or player. - getRightHalfCard().getColor().setRed(true); - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); getRightHalfCard().getSpellAbility().addTarget(new TargetCreatureOrPlayer()); getRightHalfCard().getSpellAbility().addEffect(new BloodEffect()); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/GiveTake.java b/Mage.Sets/src/mage/sets/dragonsmaze/GiveTake.java index ab375aaf048..9a9ac0ba54b 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/GiveTake.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/GiveTake.java @@ -52,20 +52,15 @@ public class GiveTake extends SplitCard { super(ownerId, 129, "Give", "Take", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2}{G}","{2}{U}", true); this.expansionSetCode = "DGM"; - this.color.setGreen(true); - this.color.setBlue(true); - // Give // Put three +1/+1 counters on target creature. getLeftHalfCard().getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(3))); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); - getLeftHalfCard().getColor().setGreen(true); // Take // Remove all +1/+1 counters from target creature you control. Draw that many cards. getRightHalfCard().getSpellAbility().addEffect(new TakeEffect()); getRightHalfCard().getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - getLeftHalfCard().getColor().setBlue(true); } public GiveTake(final GiveTake card) { diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java b/Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java index 24f21aa767a..0b751240674 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java @@ -54,7 +54,6 @@ import mage.target.targetpointer.FixedTarget; * @author LevelX2 */ - public class PossibilityStorm extends CardImpl { public PossibilityStorm(UUID ownerId) { @@ -123,7 +122,7 @@ class PossibilityStormEffect extends OneShotEffect { public PossibilityStormEffect() { super(Outcome.Neutral); - staticText = "that player exiles it, then exiles cards from the top of his or her library until he or she exiles a card that shares a card type with it. That player may cast that card without paying its mana cost. Then he or she puts all cards exiled with Possibility Storm on the bottom of his or her library in a random order"; + staticText = "that player exiles it, then exiles cards from the top of his or her library until he or she exiles a card that shares a card type with it. That player may cast that card without paying its mana cost. Then he or she puts all cards exiled with {this} on the bottom of his or her library in a random order"; } public PossibilityStormEffect(final PossibilityStormEffect effect) { @@ -148,7 +147,9 @@ class PossibilityStormEffect extends OneShotEffect { } } while (library.size() > 0 && card != null && !sharesType(card, spell.getCardType())); - if (card != null && sharesType(card, spell.getCardType())) { + if (card != null && sharesType(card, spell.getCardType()) && + !card.getCardType().contains(CardType.LAND) && + card.getSpellAbility().getTargets().canChoose(spellController.getId(), game)) { if (spellController.chooseUse(Outcome.PlayForFree, "Cast " + card.getLogName() + " without paying cost?", game)) { spellController.cast(card.getSpellAbility(), game, true); } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ProfitLoss.java b/Mage.Sets/src/mage/sets/dragonsmaze/ProfitLoss.java index afb564d344c..c5b6d8b250a 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ProfitLoss.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ProfitLoss.java @@ -57,17 +57,12 @@ public class ProfitLoss extends SplitCard { super(ownerId, 130, "Profit", "Loss", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{W}", "{2}{B}", true); this.expansionSetCode = "DGM"; - this.color.setWhite(true); - this.color.setBlack(true); - // Profit // Creatures you control get +1/+1 until end of turn. - getLeftHalfCard().getColor().setBlue(true); getLeftHalfCard().getSpellAbility().addEffect(new BoostControlledEffect(1,1, Duration.EndOfTurn, new FilterCreaturePermanent())); // Loss // Creatures your opponents control get -1/-1 until end of turn. - getRightHalfCard().getColor().setBlack(true); getRightHalfCard().getSpellAbility().addEffect(new BoostAllEffect(-1, -1, Duration.EndOfTurn, filter, false)); } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ProtectServe.java b/Mage.Sets/src/mage/sets/dragonsmaze/ProtectServe.java index 78264e782c5..56db78c159b 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ProtectServe.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ProtectServe.java @@ -48,18 +48,13 @@ public class ProtectServe extends SplitCard { super(ownerId, 131, "Protect", "Serve", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{W}", "{1}{U}", true); this.expansionSetCode = "DGM"; - this.color.setWhite(true); - this.color.setBlue(true); - // Protect // Target creature gets +2/+4 until end of turn. - getLeftHalfCard().getColor().setWhite(true); getLeftHalfCard().getSpellAbility().addEffect(new BoostTargetEffect(2,4, Duration.EndOfTurn)); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); // Serve // Target creature gets -6/-0 until end of turn. - getRightHalfCard().getColor().setBlue(true); getRightHalfCard().getSpellAbility().addEffect(new BoostTargetEffect(-6,0, Duration.EndOfTurn)); getRightHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ReadyWilling.java b/Mage.Sets/src/mage/sets/dragonsmaze/ReadyWilling.java index 727fdcdb378..d67e095eb89 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ReadyWilling.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ReadyWilling.java @@ -53,14 +53,8 @@ public class ReadyWilling extends SplitCard { super(ownerId, 132, "Ready", "Willing", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{1}{G}{W}", "{1}{W}{B}", true); this.expansionSetCode = "DGM"; - this.color.setGreen(true); - this.color.setWhite(true); - this.color.setBlack(true); - // Ready // Creatures you control are indestructible this turn. Untap each creature you control. - getLeftHalfCard().getColor().setGreen(true); - getLeftHalfCard().getColor().setWhite(true); Effect effect = new GainAbilityAllEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("Creatures you controll"), false); effect.setText("Creatures you control are indestructible this turn"); getLeftHalfCard().getSpellAbility().addEffect(effect); @@ -68,8 +62,6 @@ public class ReadyWilling extends SplitCard { // Willing // Creatures you control gain deathtouch and lifelink until end of turn. - getRightHalfCard().getColor().setWhite(true); - getRightHalfCard().getColor().setBlack(true); getRightHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn, new FilterCreaturePermanent("Creatures")) ); effect = new GainAbilityControlledEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn, new FilterCreaturePermanent("Creatures")); effect.setText("Creatures you control gain lifelink until end of turn"); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ToilTrouble.java b/Mage.Sets/src/mage/sets/dragonsmaze/ToilTrouble.java index e9ada699a40..b6b39e004bb 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ToilTrouble.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ToilTrouble.java @@ -54,19 +54,14 @@ public class ToilTrouble extends SplitCard { super(ownerId, 133, "Toil", "Trouble", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2}{B}", "{2}{R}",true); this.expansionSetCode = "DGM"; - this.color.setBlack(true); - this.color.setRed(true); - // Toil // Target player draws two cards and loses 2 life. - getLeftHalfCard().getColor().setBlack(true); getLeftHalfCard().getSpellAbility().addTarget(new TargetPlayer()); getLeftHalfCard().getSpellAbility().addEffect(new DrawCardTargetEffect(2)); getLeftHalfCard().getSpellAbility().addEffect(new LoseLifeTargetEffect(2)); // Trouble // Trouble deals damage to target player equal to the number of cards in that player's hand. - getRightHalfCard().getColor().setRed(true); Effect effect = new DamageTargetEffect(new TargetPlayerCardsInHandCount()); effect.setText("Trouble deals damage to target player equal to the number of cards in that player's hand"); getRightHalfCard().getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/TurnBurn.java b/Mage.Sets/src/mage/sets/dragonsmaze/TurnBurn.java index 01ea29ed0ab..db568c91fc8 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/TurnBurn.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/TurnBurn.java @@ -29,11 +29,9 @@ package mage.sets.dragonsmaze; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; -import mage.abilities.effects.common.continuous.LoseAllAbilitiesTargetEffect; import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Duration; @@ -52,12 +50,8 @@ public class TurnBurn extends SplitCard { super(ownerId, 134, "Turn", "Burn", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{U}", "{1}{R}", true); this.expansionSetCode = "DGM"; - this.color.setBlue(true); - this.color.setRed(true); - // Turn // Until end of turn, target creature loses all abilities and becomes a red Weird with base power and toughness 0/1. - getLeftHalfCard().getColor().setBlue(true); Effect effect = new BecomesCreatureTargetEffect(new WeirdToken(), true, false, Duration.EndOfTurn); effect.setText("Until end of turn, target creature loses all abilities and becomes a red Weird with base power and toughness 0/1"); getLeftHalfCard().getSpellAbility().addEffect(effect); @@ -65,7 +59,6 @@ public class TurnBurn extends SplitCard { // Burn // Burn deals 2 damage to target creature or player. - getRightHalfCard().getColor().setRed(true); effect = new DamageTargetEffect(2); effect.setText("Burn deals 2 damage to target creature or player"); getRightHalfCard().getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/WearTear.java b/Mage.Sets/src/mage/sets/dragonsmaze/WearTear.java index b9f6babda76..7b68dd26a31 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/WearTear.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/WearTear.java @@ -47,19 +47,14 @@ public class WearTear extends SplitCard { super(ownerId, 135, "Wear", "Tear", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{R}", "{W}", true); this.expansionSetCode = "DGM"; - this.color.setRed(true); - this.color.setWhite(true); - // Wear // Destroy target artifact. - getLeftHalfCard().getColor().setRed(true); getLeftHalfCard().getSpellAbility().addEffect(new DestroyTargetEffect()); Target target = new TargetArtifactPermanent(); getLeftHalfCard().getSpellAbility().addTarget(target); // Tear // Destroy target enchantment. - getRightHalfCard().getColor().setWhite(true); getRightHalfCard().getSpellAbility().addEffect(new DestroyTargetEffect()); target = new TargetPermanent(new FilterEnchantment()); getRightHalfCard().getSpellAbility().addTarget(target); diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/DeathmistRaptor.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/DeathmistRaptor.java index ffde7596bcb..d6295186c56 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/DeathmistRaptor.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/DeathmistRaptor.java @@ -29,6 +29,7 @@ package mage.sets.dragonsoftarkir; import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.TurnedFaceUpAllTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -63,7 +64,7 @@ public class DeathmistRaptor extends CardImpl { this.addAbility(DeathtouchAbility.getInstance()); // Whenever a permanent you control is turned face up, you may return Deathmist Raptor from your graveyard to the battlefield face up or face down. - this.addAbility(new TurnedFaceUpAllTriggeredAbility(Zone.GRAVEYARD, new DeathmistRaptorEffect(), new FilterControlledPermanent(), false, true)); + this.addAbility(new TurnedFaceUpAllTriggeredAbility(Zone.GRAVEYARD, new DeathmistRaptorEffect(), new FilterControlledPermanent("a permanent you control"), false, true)); // Megamorph {4}{G} this.addAbility(new MorphAbility(this, new ManaCostsImpl("{4}{G}"), true)); @@ -98,10 +99,10 @@ class DeathmistRaptorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Card card = game.getCard(source.getSourceId()); - if (controller != null && card != null) { - controller.putOntoBattlefieldWithInfo(card, game, Zone.GRAVEYARD, source.getSourceId(), false, - controller.chooseUse(Outcome.Detriment, "Return " + card.getName() + " face down to battlefield (otherwise face up)?", game)); + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (controller != null && (sourceObject instanceof Card)) { + controller.putOntoBattlefieldWithInfo((Card) sourceObject, game, Zone.GRAVEYARD, source.getSourceId(), false, + controller.chooseUse(Outcome.Detriment, "Return " + sourceObject.getLogName() + " face down to battlefield (otherwise face up)?", game)); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/DisplayOfDominance.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/DisplayOfDominance.java index 04b2d05bc3b..43d9ebeb73a 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/DisplayOfDominance.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/DisplayOfDominance.java @@ -124,7 +124,7 @@ class DisplayOfDominanceEffect extends ContinuousRuleModifyingEffectImpl { MageObject mageObject = game.getObject(event.getSourceId()); if (game.getPlayer(ability.getControllerId()).hasOpponent(event.getPlayerId(), game) && mageObject instanceof Spell && - (mageObject.getColor().isBlack() || mageObject.getColor().isBlue())) { + (mageObject.getColor(game).isBlack() || mageObject.getColor(game).isBlue())) { Permanent permanent = game.getPermanent(event.getTargetId()); return permanent != null && permanent.getControllerId().equals(ability.getControllerId()); } diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/OjutaiExemplars.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/OjutaiExemplars.java index a6f534e7851..dbbc8692aa3 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/OjutaiExemplars.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/OjutaiExemplars.java @@ -58,7 +58,7 @@ import mage.target.common.TargetCreaturePermanent; */ public class OjutaiExemplars extends CardImpl { - private static final FilterSpell filter = new FilterSpell("creature spell"); + private static final FilterSpell filter = new FilterSpell("a noncreature spell"); static { filter.add(Predicates.not(new CardTypePredicate(CardType.CREATURE))); diff --git a/Mage.Sets/src/mage/sets/eventide/BloomTender.java b/Mage.Sets/src/mage/sets/eventide/BloomTender.java index da2614e5d88..f4544ddc232 100644 --- a/Mage.Sets/src/mage/sets/eventide/BloomTender.java +++ b/Mage.Sets/src/mage/sets/eventide/BloomTender.java @@ -106,19 +106,19 @@ class BloomTenderEffect extends ManaEffect { if (controller != null) { Mana mana = new Mana(); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(controller.getId())) { - if (mana.getBlack() == 0 && permanent.getColor().isBlack()) { + if (mana.getBlack() == 0 && permanent.getColor(game).isBlack()) { mana.addBlack(); } - if (mana.getBlue() == 0 && permanent.getColor().isBlue()) { + if (mana.getBlue() == 0 && permanent.getColor(game).isBlue()) { mana.addBlue(); } - if (mana.getRed() == 0 && permanent.getColor().isRed()) { + if (mana.getRed() == 0 && permanent.getColor(game).isRed()) { mana.addRed(); } - if (mana.getGreen() == 0 && permanent.getColor().isGreen()) { + if (mana.getGreen() == 0 && permanent.getColor(game).isGreen()) { mana.addGreen(); } - if (mana.getWhite() == 0 && permanent.getColor().isWhite()) { + if (mana.getWhite() == 0 && permanent.getColor(game).isWhite()) { mana.addWhite(); } } diff --git a/Mage.Sets/src/mage/sets/eventide/IndigoFaerie.java b/Mage.Sets/src/mage/sets/eventide/IndigoFaerie.java index e7b6df7e5c6..ba3a0ef16ad 100644 --- a/Mage.Sets/src/mage/sets/eventide/IndigoFaerie.java +++ b/Mage.Sets/src/mage/sets/eventide/IndigoFaerie.java @@ -101,7 +101,7 @@ class BecomesBlueTargetEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { - permanent.getColor().setBlue(true); + permanent.getColor(game).setBlue(true); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/exodus/ErraticPortal.java b/Mage.Sets/src/mage/sets/exodus/ErraticPortal.java index 0e30181ea6e..49b4ef3f46b 100644 --- a/Mage.Sets/src/mage/sets/exodus/ErraticPortal.java +++ b/Mage.Sets/src/mage/sets/exodus/ErraticPortal.java @@ -95,19 +95,22 @@ class ErraticPortalEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent targetCreature = game.getPermanent(source.getFirstTarget()); - if (targetCreature != null) { - Player player = game.getPlayer(targetCreature.getControllerId()); - if (player != null) { - cost.clearPaid(); - final StringBuilder sb = new StringBuilder("Pay {1} otherwise ").append(targetCreature.getName()).append(" will be returned to its owner's hand)"); - if (player.chooseUse(Outcome.Benefit, sb.toString(), game)) { - cost.pay(source, game, targetCreature.getControllerId(), targetCreature.getControllerId(), true); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (targetCreature != null) { + Player player = game.getPlayer(targetCreature.getControllerId()); + if (player != null) { + cost.clearPaid(); + if (player.chooseUse(Outcome.Benefit, "Pay {1}? (Otherwise " + targetCreature.getLogName() +" will be returned to its owner's hand)", game)) { + cost.pay(source, game, targetCreature.getControllerId(), targetCreature.getControllerId(), false); + } + if (!cost.isPaid()) { + controller.moveCards(targetCreature, Zone.BATTLEFIELD, Zone.HAND, source, game); + } } - if (!cost.isPaid()) { - return targetCreature.moveToZone(Zone.HAND, source.getSourceId(), game, true); - } - } + } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/exodus/ExaltedDragon.java b/Mage.Sets/src/mage/sets/exodus/ExaltedDragon.java index e0931a73e79..a68578c0a68 100644 --- a/Mage.Sets/src/mage/sets/exodus/ExaltedDragon.java +++ b/Mage.Sets/src/mage/sets/exodus/ExaltedDragon.java @@ -105,7 +105,7 @@ class ExaltedDragonReplacementEffect extends ReplacementEffectImpl { if ( attackCost.canPay(source, source.getSourceId(), event.getPlayerId(), game) && player.chooseUse(Outcome.Neutral, "Sacrifice a land?", game) ) { - if (attackCost.pay(source, game, source.getSourceId(), event.getPlayerId(), true) ) { + if (attackCost.pay(source, game, source.getSourceId(), event.getPlayerId(), false) ) { return false; } } diff --git a/Mage.Sets/src/mage/sets/exodus/ShatteringPulse.java b/Mage.Sets/src/mage/sets/exodus/ShatteringPulse.java new file mode 100644 index 00000000000..fe34e1c963d --- /dev/null +++ b/Mage.Sets/src/mage/sets/exodus/ShatteringPulse.java @@ -0,0 +1,65 @@ +/* + * 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.sets.exodus; + +import java.util.UUID; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.BuybackAbility; +import mage.target.TargetPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class ShatteringPulse extends CardImpl { + + public ShatteringPulse(UUID ownerId) { + super(ownerId, 102, "Shattering Pulse", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{R}"); + this.expansionSetCode = "EXO"; + + // Buyback {3} + this.addAbility(new BuybackAbility("{3}")); + + // Destroy target artifact. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); + } + + public ShatteringPulse(final ShatteringPulse card) { + super(card); + } + + @Override + public ShatteringPulse copy() { + return new ShatteringPulse(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fatereforged/MonasteryMentor.java b/Mage.Sets/src/mage/sets/fatereforged/MonasteryMentor.java index e30f0ac8769..73871d05660 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/MonasteryMentor.java +++ b/Mage.Sets/src/mage/sets/fatereforged/MonasteryMentor.java @@ -61,6 +61,7 @@ public class MonasteryMentor extends CardImpl { // Prowess this.addAbility(new ProwessAbility()); + // Whenever you cast a noncreature spell, put a 1/1 white Monk creature token with prowess onto the battlefield. this.addAbility(new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new MonasteryMentorToken()), filter, false)); } diff --git a/Mage.Sets/src/mage/sets/fatereforged/WhisperwoodElemental.java b/Mage.Sets/src/mage/sets/fatereforged/WhisperwoodElemental.java index 2d8174c36d9..aba6948e696 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/WhisperwoodElemental.java +++ b/Mage.Sets/src/mage/sets/fatereforged/WhisperwoodElemental.java @@ -76,7 +76,7 @@ public class WhisperwoodElemental extends CardImpl { Effect effect = new GainAbilityControlledEffect(abilityToGain, Duration.EndOfTurn, filter); effect.setText("Until end of turn, face-up, nontoken creatures you control gain \"When this creature dies, manifest the top card of your library.\""); this.addAbility(new SimpleActivatedAbility( - Zone.BATTLEFIELD, effect, new SacrificeSourceCost())); + Zone.ALL, effect, new SacrificeSourceCost())); } public WhisperwoodElemental(final WhisperwoodElemental card) { diff --git a/Mage.Sets/src/mage/sets/fifthdawn/AvariceTotem.java b/Mage.Sets/src/mage/sets/fifthdawn/AvariceTotem.java index c3a165351ca..712d873c4f3 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/AvariceTotem.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/AvariceTotem.java @@ -51,7 +51,7 @@ public class AvariceTotem extends CardImpl { this.expansionSetCode = "5DN"; // {5}: Exchange control of Avarice Totem and target nonland permanent. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExchangeControlTargetEffect(Duration.EndOfGame, rule, false), new ManaCostsImpl("{5}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExchangeControlTargetEffect(Duration.EndOfGame, rule, true), new ManaCostsImpl("{5}")); ability.addTarget(new TargetNonlandPermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/fifthdawn/GrindingStation.java b/Mage.Sets/src/mage/sets/fifthdawn/GrindingStation.java index de1b9388df7..5b36e9d7db7 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/GrindingStation.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/GrindingStation.java @@ -61,7 +61,7 @@ public class GrindingStation extends CardImpl { super(ownerId, 127, "Grinding Station", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{2}"); this.expansionSetCode = "5DN"; - // {tap}, Sacrifice an artifact: Target player puts the top three cards of his or her library into his or her graveyard. + // {T}, Sacrifice an artifact: Target player puts the top three cards of his or her library into his or her graveyard. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutTopCardOfLibraryIntoGraveTargetEffect(3), new TapSourceCost()); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); ability.addTarget(new TargetPlayer()); diff --git a/Mage.Sets/src/mage/sets/fifthdawn/GuardianIdol.java b/Mage.Sets/src/mage/sets/fifthdawn/GuardianIdol.java index e8c9eec046f..2202874d7f2 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/GuardianIdol.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/GuardianIdol.java @@ -56,7 +56,7 @@ public class GuardianIdol extends CardImpl { // {tap}: Add {1} to your mana pool. this.addAbility(new ColorlessManaAbility()); // {2}: Guardian Idol becomes a 2/2 Golem artifact creature until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new GolemToken(), "", Duration.EndOfTurn), new ManaCostsImpl("{2}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new GuardianIdolGolemToken(), "", Duration.EndOfTurn), new ManaCostsImpl("{2}"))); } public GuardianIdol(final GuardianIdol card) { @@ -69,9 +69,9 @@ public class GuardianIdol extends CardImpl { } } -class GolemToken extends Token { +class GuardianIdolGolemToken extends Token { - public GolemToken() { + public GuardianIdolGolemToken() { super("Golem", "a 2/2 Golem artifact creature token"); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); diff --git a/Mage.Sets/src/mage/sets/fifthdawn/MycosynthGolem.java b/Mage.Sets/src/mage/sets/fifthdawn/MycosynthGolem.java index acd7225b6b0..084fbf7fc3e 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/MycosynthGolem.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/MycosynthGolem.java @@ -29,27 +29,39 @@ package mage.sets.fifthdawn; import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.keyword.AffinityForArtifactsAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.Rarity; +import mage.constants.SubLayer; import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; -import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.players.Player; /** * * @author jeffwadsworth */ public class MycosynthGolem extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("Artifact creature spells you cast"); + static { + filter.add(new CardTypePredicate(CardType.ARTIFACT)); + filter.add(new CardTypePredicate(CardType.CREATURE)); + } + public MycosynthGolem(UUID ownerId) { super(ownerId, 137, "Mycosynth Golem", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{11}"); this.expansionSetCode = "5DN"; @@ -62,7 +74,8 @@ public class MycosynthGolem extends CardImpl { this.addAbility(new AffinityForArtifactsAbility()); // Artifact creature spells you cast have affinity for artifacts. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MycosynthGolemEffect())); + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, new MycosynthGolemGainAbilitySpellsEffect(new AffinityForArtifactsAbility(), filter))); } @@ -76,56 +89,46 @@ public class MycosynthGolem extends CardImpl { } } -class MycosynthGolemEffect extends ReplacementEffectImpl { +class MycosynthGolemGainAbilitySpellsEffect extends ContinuousEffectImpl { - public MycosynthGolemEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Artifact creature spells you cast have affinity for artifacts"; + private final Ability ability; + private final FilterSpell filter; + + public MycosynthGolemGainAbilitySpellsEffect(Ability ability, FilterSpell filter) { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + this.ability = ability; + this.filter = filter; + staticText = filter.getMessage() + " have " + ability.getRule(); } - public MycosynthGolemEffect(final MycosynthGolemEffect effect) { + public MycosynthGolemGainAbilitySpellsEffect(final MycosynthGolemGainAbilitySpellsEffect effect) { super(effect); + this.ability = effect.ability; + this.filter = effect.filter; } @Override - public MycosynthGolemEffect copy() { - return new MycosynthGolemEffect(this); + public MycosynthGolemGainAbilitySpellsEffect copy() { + return new MycosynthGolemGainAbilitySpellsEffect(this); } @Override public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - MageObject object = game.getObject(event.getSourceId()); - if (object != null) { - Card card = (Card) object; - Ability ability = new AffinityForArtifactsAbility(); - game.getState().addOtherAbility(card, ability); - ability.setControllerId(source.getControllerId()); - ability.setSourceId(card.getId()); - game.getState().addAbility(ability, source.getSourceId(), card); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.CAST_SPELL; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if ((event.getType() == GameEvent.EventType.CAST_SPELL) - && event.getPlayerId() == source.getControllerId()) { - MageObject spellObject = game.getObject(event.getSourceId()); - if (spellObject != null - && spellObject.getCardType().contains(CardType.CREATURE) - && spellObject.getCardType().contains(CardType.ARTIFACT)) { - return true; + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (player != null && permanent != null) { + for (StackObject stackObject : game.getStack()) { + // only spells cast, so no copies of spells + if ((stackObject instanceof Spell) && !stackObject.isCopy() && stackObject.getControllerId().equals(source.getControllerId())) { + Spell spell = (Spell) stackObject; + if (filter.match(spell, game)) { + if (!spell.getAbilities().contains(ability)) { + game.getState().addOtherAbility(spell.getCard(), ability); + } + } + } } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/fifthdawn/PlungeIntoDarkness.java b/Mage.Sets/src/mage/sets/fifthdawn/PlungeIntoDarkness.java index 8e3b700106f..1d8c1a7430f 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/PlungeIntoDarkness.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/PlungeIntoDarkness.java @@ -148,7 +148,7 @@ class PlungeIntoDarknessSearchEffect extends OneShotEffect { if (player != null) { VariableCost cost = new PayVariableLifeCost(); int xValue = cost.announceXValue(source, game); - cost.getFixedCostsFromAnnouncedValue(xValue).pay(source, game, source.getSourceId(), source.getControllerId(), true); + cost.getFixedCostsFromAnnouncedValue(xValue).pay(source, game, source.getSourceId(), source.getControllerId(), false); Cards cards = new CardsImpl(Zone.PICK); int count = Math.min(player.getLibrary().size(), xValue); diff --git a/Mage.Sets/src/mage/sets/fifthdawn/TelJiladJustice.java b/Mage.Sets/src/mage/sets/fifthdawn/TelJiladJustice.java index 37cfafa7776..5e09b49285b 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/TelJiladJustice.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/TelJiladJustice.java @@ -45,7 +45,6 @@ public class TelJiladJustice extends CardImpl { super(ownerId, 95, "Tel-Jilad Justice", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{G}"); this.expansionSetCode = "5DN"; - // Destroy target artifact. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetArtifactPermanent()); diff --git a/Mage.Sets/src/mage/sets/fifthedition/ClayStatue.java b/Mage.Sets/src/mage/sets/fifthedition/ClayStatue.java new file mode 100644 index 00000000000..063a53eac8b --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/ClayStatue.java @@ -0,0 +1,53 @@ +/* + * 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.sets.fifthedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class ClayStatue extends mage.sets.antiquities.ClayStatue { + + public ClayStatue(UUID ownerId) { + super(ownerId); + this.cardNumber = 355; + this.expansionSetCode = "5ED"; + } + + public ClayStatue(final ClayStatue card) { + super(card); + } + + @Override + public ClayStatue copy() { + return new ClayStatue(this); + } +} + diff --git a/Mage.Sets/src/mage/sets/fifthedition/Hydroblast.java b/Mage.Sets/src/mage/sets/fifthedition/Hydroblast.java index fbc117f5c72..00e1a79ae3d 100644 --- a/Mage.Sets/src/mage/sets/fifthedition/Hydroblast.java +++ b/Mage.Sets/src/mage/sets/fifthedition/Hydroblast.java @@ -89,7 +89,7 @@ class HydroblastCounterEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - if (game.getStack().getSpell(source.getFirstTarget()).getColor().isRed()) { + if (game.getStack().getSpell(source.getFirstTarget()).getColor(game).isRed()) { game.getStack().counter(source.getFirstTarget(), source.getSourceId(), game); } return true; @@ -119,7 +119,7 @@ class HydroblastDestroyEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getTargets().getFirstTarget()); - if (permanent != null && permanent.getColor().isRed()) { + if (permanent != null && permanent.getColor(game).isRed()) { permanent.destroy(source.getSourceId(), game, false); } return true; diff --git a/Mage.Sets/src/mage/sets/fifthedition/Justice.java b/Mage.Sets/src/mage/sets/fifthedition/Justice.java index ff880f4384c..ffa932141df 100644 --- a/Mage.Sets/src/mage/sets/fifthedition/Justice.java +++ b/Mage.Sets/src/mage/sets/fifthedition/Justice.java @@ -95,7 +95,7 @@ class JusticeTriggeredAbility extends TriggeredAbilityImpl { || event.getType() == GameEvent.EventType.DAMAGED_PLAYER || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER) { MageObject sourceObject = game.getObject(event.getSourceId()); - if (sourceObject.getColor().isRed()) { + if (sourceObject.getColor(game).isRed()) { if (sourceObject instanceof Permanent && sourceObject.getCardType().contains(CardType.CREATURE) || sourceObject instanceof Spell) { this.getEffects().get(0).setValue("damageAmount", event.getAmount()); diff --git a/Mage.Sets/src/mage/sets/fifthedition/WallOfBrambles.java b/Mage.Sets/src/mage/sets/fifthedition/WallOfBrambles.java new file mode 100644 index 00000000000..823d6e68494 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/WallOfBrambles.java @@ -0,0 +1,52 @@ +/* + * 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.sets.fifthedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class WallOfBrambles extends mage.sets.limitedalpha.WallOfBrambles { + + public WallOfBrambles(UUID ownerId) { + super(ownerId); + this.cardNumber = 200; + this.expansionSetCode = "5ED"; + } + + public WallOfBrambles(final WallOfBrambles card) { + super(card); + } + + @Override + public WallOfBrambles copy() { + return new WallOfBrambles(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fourthedition/ClayStatue.java b/Mage.Sets/src/mage/sets/fourthedition/ClayStatue.java new file mode 100644 index 00000000000..7924167129b --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/ClayStatue.java @@ -0,0 +1,53 @@ +/* + * 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.sets.fourthedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class ClayStatue extends mage.sets.antiquities.ClayStatue { + + public ClayStatue(UUID ownerId) { + super(ownerId); + this.cardNumber = 323; + this.expansionSetCode = "4ED"; + } + + public ClayStatue(final ClayStatue card) { + super(card); + } + + @Override + public ClayStatue copy() { + return new ClayStatue(this); + } +} + diff --git a/Mage.Sets/src/mage/sets/fourthedition/Fissure.java b/Mage.Sets/src/mage/sets/fourthedition/Fissure.java new file mode 100644 index 00000000000..6c0e119468f --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/Fissure.java @@ -0,0 +1,52 @@ +/* + * 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.sets.fourthedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class Fissure extends mage.sets.thedark.Fissure { + + public Fissure(UUID ownerId) { + super(ownerId); + this.cardNumber = 212; + this.expansionSetCode = "4ED"; + } + + public Fissure(final Fissure card) { + super(card); + } + + @Override + public Fissure copy() { + return new Fissure(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fourthedition/WallOfBrambles.java b/Mage.Sets/src/mage/sets/fourthedition/WallOfBrambles.java new file mode 100644 index 00000000000..8e690dfa3d3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/WallOfBrambles.java @@ -0,0 +1,52 @@ +/* + * 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.sets.fourthedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class WallOfBrambles extends mage.sets.limitedalpha.WallOfBrambles { + + public WallOfBrambles(UUID ownerId) { + super(ownerId); + this.cardNumber = 166; + this.expansionSetCode = "4ED"; + } + + public WallOfBrambles(final WallOfBrambles card) { + super(card); + } + + @Override + public WallOfBrambles copy() { + return new WallOfBrambles(this); + } +} diff --git a/Mage.Sets/src/mage/sets/futuresight/DryadArbor.java b/Mage.Sets/src/mage/sets/futuresight/DryadArbor.java index 19da16a3ccf..9de739559ab 100644 --- a/Mage.Sets/src/mage/sets/futuresight/DryadArbor.java +++ b/Mage.Sets/src/mage/sets/futuresight/DryadArbor.java @@ -49,7 +49,7 @@ public class DryadArbor extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.getColor().setGreen(true); + this.color.setGreen(true); // (Dryad Arbor isn't a spell, it's affected by summoning sickness, and it has "{tap}: Add {G} to your mana pool.") this.addAbility(new GreenManaAbility()); diff --git a/Mage.Sets/src/mage/sets/futuresight/GlitteringWish.java b/Mage.Sets/src/mage/sets/futuresight/GlitteringWish.java index cff98bf319b..3f4ea5a174f 100644 --- a/Mage.Sets/src/mage/sets/futuresight/GlitteringWish.java +++ b/Mage.Sets/src/mage/sets/futuresight/GlitteringWish.java @@ -82,7 +82,7 @@ class GlitteringWishEffect extends OneShotEffect { @Override public boolean apply(MageObject input, Game game) { - return input.getColor().isMulticolored(); + return input.getColor(game).isMulticolored(); } @Override diff --git a/Mage.Sets/src/mage/sets/futuresight/HomingSliver.java b/Mage.Sets/src/mage/sets/futuresight/HomingSliver.java index 1ec2b43aa51..31541ddd248 100644 --- a/Mage.Sets/src/mage/sets/futuresight/HomingSliver.java +++ b/Mage.Sets/src/mage/sets/futuresight/HomingSliver.java @@ -29,15 +29,12 @@ package mage.sets.futuresight; import java.util.UUID; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.common.DiscardSourceCost; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.CyclingAbility; -import mage.abilities.keyword.FlashbackAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -46,18 +43,17 @@ import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.SubLayer; -import mage.constants.TimingRule; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.Game; import mage.players.Player; /** * - * @author anonymous + * @author Luna Skyrise */ + public class HomingSliver extends CardImpl { private static final FilterCard filter = new FilterCard("Sliver card"); @@ -74,31 +70,10 @@ public class HomingSliver extends CardImpl { this.toughness = new MageInt(2); // Each Sliver card in each player's hand has slivercycling {3}. - Ability ability = new CyclingAbility(new ManaCostsImpl("{3}"), filter, "Slivercycling"); - ability.addCost(new DiscardSourceCost()); - this.addAbility(new SimpleStaticAbility(Zone.HAND, new HomingSliverEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new HomingSliverEffect())); + // Slivercycling {3} - this.addAbility(ability); - /** - * 01/02/2009 Slivercycling is a form of cycling. Any ability that - * triggers on a card being cycled also triggers on Slivercycling this - * card. Any ability that stops a cycling ability from being activated - * also stops Plainscycling from being activated. - */ - - /** - * 01/02/2009 Slivercycling is an activated ability. Effects that - * interact with activated abilities (such as Stifle or Rings of - * Brighthearth) will interact with Slivercycling. Effects that interact - * with spells (such as Remove Soul or Faerie Tauntings) will not. - */ - /** - * 01/02/2009 You can choose to find any card with the Sliver creature - * type, even if it isn't a creature card. This includes, for example, - * Tribal cards with the Changeling ability. You can also choose not to - * find a card, even if there is a Sliver card in your graveyard. - * - */ + this.addAbility(new CyclingAbility(new ManaCostsImpl("{3}"), filter, "Slivercycling")); } public HomingSliver(final HomingSliver card) { @@ -113,15 +88,15 @@ public class HomingSliver extends CardImpl { class HomingSliverEffect extends ContinuousEffectImpl { - private static final FilterCard filter2 = new FilterCard("Sliver card"); + private static final FilterCard filter = new FilterCard("Sliver card"); static { - filter2.add(new SubtypePredicate("Sliver")); + filter.add(new SubtypePredicate("Sliver")); } public HomingSliverEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); - this.staticText = "Each Sliver card in each player's hand has slivercycling {3}"; + this.staticText = "each Sliver card in each player's hand has slivercycling {3}"; } public HomingSliverEffect(final HomingSliverEffect effect) { @@ -133,45 +108,19 @@ class HomingSliverEffect extends ContinuousEffectImpl { return new HomingSliverEffect(this); } - @Override - public void init(Ability source, Game game) { - super.init(source, game); - if (this.affectedObjectsSet) { - for (UUID p : game.getPlayerList()) { - Player player = game.getPlayer(p); - if (player != null) { - for (UUID cardId : player.getHand()) { - Card card = game.getCard(cardId); - if (card.getSubtype().contains("Sliver")) { - affectedObjectList.add(new MageObjectReference(card, game)); - } - } - } - } - } - } - @Override public boolean apply(Game game, Ability source) { - for (UUID p : game.getPlayerList()) { - Player player = game.getPlayer(p); - if (player != null) { - for (UUID cardId : player.getHand()) { - if (affectedObjectList.contains(new MageObjectReference(cardId, game))) { - Card card = game.getCard(cardId); - CyclingAbility ability = null; - if (card.hasSubtype("Sliver")) { - ability = new CyclingAbility(new ManaCostsImpl("{3}"), filter2, "Slivercycling"); - } - if (ability != null) { - ability.setSourceId(cardId); - ability.setControllerId(card.getOwnerId()); - game.getState().addOtherAbility(card, ability); - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId: controller.getInRange()) { + Player player = game.getPlayer(playerId); + if (player != null) { + for (Card card : player.getHand().getCards(filter, game)) { + game.getState().addOtherAbility(card, new CyclingAbility(new GenericManaCost(3), filter, "Slivercycling")); } - } - return true; + } } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/futuresight/RiverOfTears.java b/Mage.Sets/src/mage/sets/futuresight/RiverOfTears.java index 7ba8ddb7251..881bc577850 100644 --- a/Mage.Sets/src/mage/sets/futuresight/RiverOfTears.java +++ b/Mage.Sets/src/mage/sets/futuresight/RiverOfTears.java @@ -50,7 +50,7 @@ public class RiverOfTears extends CardImpl { super(ownerId, 179, "River of Tears", Rarity.RARE, new CardType[]{CardType.LAND}, ""); this.expansionSetCode = "FUT"; - // {tap}: Add {U} to your mana pool. If you played a land this turn, add {B} to your mana pool instead. + // {T}: Add {U} to your mana pool. If you played a land this turn, add {B} to your mana pool instead. this.addAbility(new ConditionalManaAbility(Zone.BATTLEFIELD, new ConditionalManaEffect( new BasicManaEffect(Mana.BlackMana), new BasicManaEffect(Mana.BlueMana), diff --git a/Mage.Sets/src/mage/sets/gatecrash/LazavDimirMastermind.java b/Mage.Sets/src/mage/sets/gatecrash/LazavDimirMastermind.java index e795b43c9ef..b3172590881 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/LazavDimirMastermind.java +++ b/Mage.Sets/src/mage/sets/gatecrash/LazavDimirMastermind.java @@ -120,7 +120,7 @@ class LazavDimirEffect extends ContinuousEffectImpl { } permanent.getPower().setValue(cardToCopy.getPower().getValue()); permanent.getToughness().setValue(cardToCopy.getToughness().getValue()); - permanent.getColor().setColor(cardToCopy.getColor()); + permanent.getColor(game).setColor(cardToCopy.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(cardToCopy.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage.Sets/src/mage/sets/guildpact/ShatteringSpree.java b/Mage.Sets/src/mage/sets/guildpact/ShatteringSpree.java index 8d445861e8d..f84eca751d1 100644 --- a/Mage.Sets/src/mage/sets/guildpact/ShatteringSpree.java +++ b/Mage.Sets/src/mage/sets/guildpact/ShatteringSpree.java @@ -46,7 +46,6 @@ public class ShatteringSpree extends CardImpl { super(ownerId, 75, "Shattering Spree", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{R}"); this.expansionSetCode = "GPT"; - // Replicate {R} this.addAbility(new ReplicateAbility(this, "{R}")); // Destroy target artifact. diff --git a/Mage.Sets/src/mage/sets/iceage/BlackScarab.java b/Mage.Sets/src/mage/sets/iceage/BlackScarab.java new file mode 100644 index 00000000000..3ced830ddc5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/BlackScarab.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.sets.iceage; + +import java.util.Set; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Zeplar1_at_googlemail.com + */ +public class BlackScarab extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("black creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLACK)); + } + + public BlackScarab(UUID ownerId) { + super(ownerId, 230, "Black Scarab", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + this.expansionSetCode = "ICE"; + this.subtype.add("Aura"); + +// Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature can't be blocked by black creatures. + Effect effect = new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, filter, AttachmentType.AURA); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 as long as an opponent controls a black permanent. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield), + new BlackScarabCondition(), "Enchanted creature gets +2/+2 as long as an opponent controls a black permanent"))); + } + + public BlackScarab(final BlackScarab card) { + super(card); + } + + @Override + public BlackScarab copy() { + return new BlackScarab(this); + } +} + +class BlackScarabCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + boolean conditionApplies = false; + FilterPermanent filter = new FilterPermanent(); + filter.add(new ColorPredicate(ObjectColor.BLACK)); + Set opponents = game.getOpponents(source.getControllerId()); + for (UUID opponentId : opponents) { + conditionApplies |= game.getBattlefield().countAll(filter, opponentId, game) > 0; + } + return conditionApplies; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/iceage/BlueScarab.java b/Mage.Sets/src/mage/sets/iceage/BlueScarab.java new file mode 100644 index 00000000000..f2d82c989c2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/BlueScarab.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.sets.iceage; + +import java.util.Set; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Zeplar1_at_googlemail.com + */ +public class BlueScarab extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("blue creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + } + + public BlueScarab(UUID ownerId) { + super(ownerId, 233, "Blue Scarab", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + this.expansionSetCode = "ICE"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature can't be blocked by blue creatures. + Effect effect = new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, filter, AttachmentType.AURA); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 as long as an opponent controls a blue permanent. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield), + new BlueScarabCondition(), "Enchanted creature gets +2/+2 as long as an opponent controls a blue permanent"))); + } + + public BlueScarab(final BlueScarab card) { + super(card); + } + + @Override + public BlueScarab copy() { + return new BlueScarab(this); + } +} + +class BlueScarabCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + boolean conditionApplies = false; + FilterPermanent filter = new FilterPermanent(); + filter.add(new ColorPredicate(ObjectColor.BLUE)); + Set opponents = game.getOpponents(source.getControllerId()); + for (UUID opponentId : opponents) { + conditionApplies |= game.getBattlefield().countAll(filter, opponentId, game) > 0; + } + return conditionApplies; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/iceage/GreenScarab.java b/Mage.Sets/src/mage/sets/iceage/GreenScarab.java new file mode 100644 index 00000000000..d3d846e2ad5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/GreenScarab.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.sets.iceage; + +import java.util.Set; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Zeplar1_at_googlemail.com + */ +public class GreenScarab extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("green creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.GREEN)); + } + + public GreenScarab(UUID ownerId) { + super(ownerId, 252, "Green Scarab", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + this.expansionSetCode = "ICE"; + this.subtype.add("Aura"); + +// Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature can't be blocked by green creatures. + Effect effect = new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, filter, AttachmentType.AURA); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 as long as an opponent controls a white permanent. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield), + new GreenScarabCondition(), "Enchanted creature gets +2/+2 as long as an opponent controls a green permanent"))); + } + + public GreenScarab(final GreenScarab card) { + super(card); + } + + @Override + public GreenScarab copy() { + return new GreenScarab(this); + } +} + +class GreenScarabCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + boolean conditionApplies = false; + FilterPermanent filter = new FilterPermanent(); + filter.add(new ColorPredicate(ObjectColor.GREEN)); + Set opponents = game.getOpponents(source.getControllerId()); + for (UUID opponentId : opponents) { + conditionApplies |= game.getBattlefield().countAll(filter, opponentId, game) > 0; + } + return conditionApplies; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/iceage/Pyroblast.java b/Mage.Sets/src/mage/sets/iceage/Pyroblast.java index 536577b0297..04784cb21bf 100644 --- a/Mage.Sets/src/mage/sets/iceage/Pyroblast.java +++ b/Mage.Sets/src/mage/sets/iceage/Pyroblast.java @@ -89,7 +89,7 @@ class PyroblastCounterTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - if(game.getStack().getSpell(source.getFirstTarget()).getColor().isBlue()){ + if(game.getStack().getSpell(source.getFirstTarget()).getColor(game).isBlue()){ game.getStack().counter(source.getFirstTarget(), source.getSourceId(), game); } return true; @@ -122,7 +122,7 @@ class DestroyTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getTargets().getFirstTarget()); - if (permanent != null && permanent.getColor().isBlue()) { + if (permanent != null && permanent.getColor(game).isBlue()) { permanent.destroy(source.getSourceId(), game, false); } return true; diff --git a/Mage.Sets/src/mage/sets/iceage/RedScarab.java b/Mage.Sets/src/mage/sets/iceage/RedScarab.java new file mode 100644 index 00000000000..1cda24c5966 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/RedScarab.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.sets.iceage; + +import java.util.Set; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Zeplar1_at_googlemail.com + */ +public class RedScarab extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("red creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + } + + public RedScarab(UUID ownerId) { + super(ownerId, 273, "Red Scarab", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + this.expansionSetCode = "ICE"; + this.subtype.add("Aura"); + +// Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature can't be blocked by red creatures. + Effect effect = new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, filter, AttachmentType.AURA); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 as long as an opponent controls a red permanent. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield), + new RedScarabCondition(), "Enchanted creature gets +2/+2 as long as an opponent controls a red permanent"))); + } + + public RedScarab(final RedScarab card) { + super(card); + } + + @Override + public RedScarab copy() { + return new RedScarab(this); + } +} + +class RedScarabCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + boolean conditionApplies = false; + FilterPermanent filter = new FilterPermanent(); + filter.add(new ColorPredicate(ObjectColor.RED)); + Set opponents = game.getOpponents(source.getControllerId()); + for (UUID opponentId : opponents) { + conditionApplies |= game.getBattlefield().countAll(filter, opponentId, game) > 0; + } + return conditionApplies; + } +} diff --git a/Mage.Sets/src/mage/sets/iceage/WhiteScarab.java b/Mage.Sets/src/mage/sets/iceage/WhiteScarab.java new file mode 100644 index 00000000000..72429ccc8c1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/WhiteScarab.java @@ -0,0 +1,114 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.iceage; + +import java.util.Set; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; +import mage.abilities.effects.Effect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Zeplar1_at_googlemail.com + */ +public class WhiteScarab extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("white creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.WHITE)); + } + + + public WhiteScarab(UUID ownerId) { + super(ownerId, 280, "White Scarab", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + this.expansionSetCode = "ICE"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature can't be blocked by white creatures. + Effect effect = new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, filter, AttachmentType.AURA); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 as long as an opponent controls a white permanent. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield), + new WhiteScarabCondition(), "Enchanted creature gets +2/+2 as long as an opponent controls a white permanent"))); + } + + public WhiteScarab(final WhiteScarab card) { + super(card); + } + + @Override + public WhiteScarab copy() { + return new WhiteScarab(this); + } +} + +class WhiteScarabCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + boolean conditionApplies = false; + FilterPermanent filter = new FilterPermanent(); + filter.add(new ColorPredicate(ObjectColor.WHITE)); + Set opponents = game.getOpponents(source.getControllerId()); + for (UUID opponentId : opponents) { + conditionApplies |= game.getBattlefield().countAll(filter, opponentId, game) > 0; + } + return conditionApplies; + } +} diff --git a/Mage.Sets/src/mage/sets/innistrad/AncientGrudge.java b/Mage.Sets/src/mage/sets/innistrad/AncientGrudge.java index 8936c752c6b..54a7f3404ef 100644 --- a/Mage.Sets/src/mage/sets/innistrad/AncientGrudge.java +++ b/Mage.Sets/src/mage/sets/innistrad/AncientGrudge.java @@ -34,8 +34,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.FlashbackAbility; import mage.cards.CardImpl; import mage.constants.TimingRule; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; import java.util.UUID; @@ -44,21 +43,13 @@ import java.util.UUID; * @author nantuko */ public class AncientGrudge extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public AncientGrudge(UUID ownerId) { super(ownerId, 127, "Ancient Grudge", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{R}"); this.expansionSetCode = "ISD"; - // Destroy target artifact. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); // Flashback {G} this.addAbility(new FlashbackAbility(new ManaCostsImpl("{G}"), TimingRule.INSTANT)); diff --git a/Mage.Sets/src/mage/sets/innistrad/CurseOfDeathsHold.java b/Mage.Sets/src/mage/sets/innistrad/CurseOfDeathsHold.java index 1de846c25ec..2e1decc2ca4 100644 --- a/Mage.Sets/src/mage/sets/innistrad/CurseOfDeathsHold.java +++ b/Mage.Sets/src/mage/sets/innistrad/CurseOfDeathsHold.java @@ -54,7 +54,6 @@ public class CurseOfDeathsHold extends CardImpl { this.subtype.add("Aura"); this.subtype.add("Curse"); - // Enchant player TargetPlayer auraTarget = new TargetPlayer(); this.getSpellAbility().addTarget(auraTarget); @@ -106,5 +105,4 @@ class CurseOfDeathsHoldEffect extends ContinuousEffectImpl { public CurseOfDeathsHoldEffect copy() { return new CurseOfDeathsHoldEffect(this); } - -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/innistrad/EssenceOfTheWild.java b/Mage.Sets/src/mage/sets/innistrad/EssenceOfTheWild.java index 90844048974..657f77a94c3 100644 --- a/Mage.Sets/src/mage/sets/innistrad/EssenceOfTheWild.java +++ b/Mage.Sets/src/mage/sets/innistrad/EssenceOfTheWild.java @@ -137,7 +137,7 @@ class EssenceOfTheWildCopyEffect extends ContinuousEffectImpl { Permanent permanent = game.getPermanent(targetId); if (permanent != null) { permanent.setName(essence.getName()); - permanent.getColor().setColor(essence.getColor()); + permanent.getColor(game).setColor(essence.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(essence.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage.Sets/src/mage/sets/innistrad/GrimoireOfTheDead.java b/Mage.Sets/src/mage/sets/innistrad/GrimoireOfTheDead.java index 1e986905fd4..84e2bdea4ef 100644 --- a/Mage.Sets/src/mage/sets/innistrad/GrimoireOfTheDead.java +++ b/Mage.Sets/src/mage/sets/innistrad/GrimoireOfTheDead.java @@ -146,7 +146,7 @@ class GrimoireOfTheDeadEffect2 extends ContinuousEffectImpl { switch (layer) { case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - permanent.getColor().setBlack(true); + permanent.getColor(game).setBlack(true); } break; case TypeChangingEffects_4: diff --git a/Mage.Sets/src/mage/sets/innistrad/LaboratoryManiac.java b/Mage.Sets/src/mage/sets/innistrad/LaboratoryManiac.java index a5587cda001..ee8f1a919f5 100644 --- a/Mage.Sets/src/mage/sets/innistrad/LaboratoryManiac.java +++ b/Mage.Sets/src/mage/sets/innistrad/LaboratoryManiac.java @@ -113,10 +113,7 @@ class LaboratoryManiacEffect extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getPlayerId().equals(source.getControllerId())) { Player player = game.getPlayer(event.getPlayerId()); - if (!player.hasLost() && ( - (player.getLife() > 0 || !player.canLoseByZeroOrLessLife()) - && player.isEmptyDraw() - && player.getCounters().getCount(CounterType.POISON) < 10)) { + if (player != null && !player.hasLost() && player.isEmptyDraw()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/invasion/AssaultBattery.java b/Mage.Sets/src/mage/sets/invasion/AssaultBattery.java index b658ba52cc7..7eb110ec646 100644 --- a/Mage.Sets/src/mage/sets/invasion/AssaultBattery.java +++ b/Mage.Sets/src/mage/sets/invasion/AssaultBattery.java @@ -50,12 +50,8 @@ public class AssaultBattery extends SplitCard { super(ownerId, 295, "Assault", "Battery", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{R}", "{3}{G}", false); this.expansionSetCode = "INV"; - this.color.setRed(true); - this.color.setGreen(true); - // Assault // Assault deals 2 damage to target creature or player. - getLeftHalfCard().getColor().setRed(true); Effect effect = new DamageTargetEffect(2); effect.setText("Assault deals 2 damage to target creature or player"); getLeftHalfCard().getSpellAbility().addEffect(effect); @@ -63,7 +59,6 @@ public class AssaultBattery extends SplitCard { // Battery // Put a 3/3 green Elephant creature token onto the battlefield. - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new ElephantToken())); } diff --git a/Mage.Sets/src/mage/sets/invasion/DismantlingBlow.java b/Mage.Sets/src/mage/sets/invasion/DismantlingBlow.java index 7d6e4f246f9..7d3e1c221d3 100644 --- a/Mage.Sets/src/mage/sets/invasion/DismantlingBlow.java +++ b/Mage.Sets/src/mage/sets/invasion/DismantlingBlow.java @@ -36,9 +36,7 @@ import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -47,24 +45,15 @@ import mage.target.TargetPermanent; */ public class DismantlingBlow extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public DismantlingBlow(UUID ownerId) { super(ownerId, 14, "Dismantling Blow", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{W}"); this.expansionSetCode = "INV"; - // Kicker {2}{U} this.addAbility(new KickerAbility("{2}{U}")); // Destroy target artifact or enchantment. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); // If Dismantling Blow was kicked, draw two cards. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new DrawCardSourceControllerEffect(2), diff --git a/Mage.Sets/src/mage/sets/invasion/ShivanZombie.java b/Mage.Sets/src/mage/sets/invasion/ShivanZombie.java new file mode 100644 index 00000000000..3c11fb51a77 --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/ShivanZombie.java @@ -0,0 +1,70 @@ +/* + * 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.sets.invasion; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.keyword.ProtectionAbility; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class ShivanZombie extends CardImpl { + private static final FilterCard protectionFilter = new FilterCard("white"); + static { + protectionFilter.add(new ColorPredicate(ObjectColor.WHITE)); + } + + public ShivanZombie(UUID ownerId) { + super(ownerId, 271, "Shivan Zombie", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{B}{R}"); + this.expansionSetCode = "INV"; + this.subtype.add("Barbarian"); + this.subtype.add("Zombie"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Protection from white + this.addAbility(new ProtectionAbility(protectionFilter)); + } + + public ShivanZombie(final ShivanZombie card) { + super(card); + } + + @Override + public ShivanZombie copy() { + return new ShivanZombie(this); + } +} diff --git a/Mage.Sets/src/mage/sets/invasion/VodalianZombie.java b/Mage.Sets/src/mage/sets/invasion/VodalianZombie.java new file mode 100644 index 00000000000..e904e1a4d33 --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/VodalianZombie.java @@ -0,0 +1,70 @@ +/* + * 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.sets.invasion; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.keyword.ProtectionAbility; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class VodalianZombie extends CardImpl { + private static final FilterCard protectionFilter = new FilterCard("green"); + static { + protectionFilter.add(new ColorPredicate(ObjectColor.GREEN)); + } + + public VodalianZombie(UUID ownerId) { + super(ownerId, 286, "Vodalian Zombie", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{U}{B}"); + this.expansionSetCode = "INV"; + this.subtype.add("Merfolk"); + this.subtype.add("Zombie"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Protection from green + this.addAbility(new ProtectionAbility(protectionFilter)); + } + + public VodalianZombie(final VodalianZombie card) { + super(card); + } + + @Override + public VodalianZombie copy() { + return new VodalianZombie(this); + } +} diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/HallOfTriumph.java b/Mage.Sets/src/mage/sets/journeyintonyx/HallOfTriumph.java index 9b93ba4a784..8e4ad8a781f 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/HallOfTriumph.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/HallOfTriumph.java @@ -138,7 +138,7 @@ class HallOfTriumphBoostControlledEffect extends ContinuousEffectImpl { ObjectColor color = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color"); if (color != null) { for (Permanent perm: game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { - if (perm.getColor().shares(color)) { + if (perm.getColor(game).shares(color)) { perm.addPower(1); perm.addToughness(1); } diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java b/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java index 8f18a5ff389..f94df13e691 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java @@ -27,6 +27,9 @@ */ package mage.sets.journeyintonyx; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; @@ -44,12 +47,14 @@ import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; import mage.constants.Rarity; +import mage.constants.WatcherScope; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreatureOrPlayer; +import mage.watchers.Watcher; /** * @@ -77,7 +82,7 @@ public class KeranosGodOfStorms extends CardImpl { // Reveal the first card you draw on each of your turns. // Whenever you reveal a land card this way, draw a card. // Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player. - this.addAbility(new KeranosGodOfStormsTriggeredAbility()); + this.addAbility(new KeranosGodOfStormsTriggeredAbility(), new CardsDrawnDuringTurnWatcher()); } @@ -94,15 +99,12 @@ public class KeranosGodOfStorms extends CardImpl { class KeranosGodOfStormsTriggeredAbility extends TriggeredAbilityImpl { - private int lastTriggeredTurn; - KeranosGodOfStormsTriggeredAbility() { super(Zone.BATTLEFIELD, new InfoEffect(""), false); } KeranosGodOfStormsTriggeredAbility(final KeranosGodOfStormsTriggeredAbility ability) { super(ability); - this.lastTriggeredTurn = ability.lastTriggeredTurn; } @Override @@ -113,13 +115,16 @@ class KeranosGodOfStormsTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.DREW_CARD && event.getPlayerId().equals(this.getControllerId())) { - if (game.getActivePlayerId().equals(this.getControllerId()) && this.lastTriggeredTurn != game.getTurnNum()) { + if (game.getActivePlayerId().equals(this.getControllerId())) { + CardsDrawnDuringTurnWatcher watcher = (CardsDrawnDuringTurnWatcher) game.getState().getWatchers().get("CardsDrawnDuringTurn"); + if (watcher != null && watcher.getAmountCardsDrawn(event.getPlayerId()) != 1) { + return false; + } Card card = game.getCard(event.getTargetId()); Player controller = game.getPlayer(this.getControllerId()); - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(this.getSourceId()); + Permanent sourcePermanent = (Permanent) getSourceObject(game); if (card != null && controller != null && sourcePermanent != null) { - lastTriggeredTurn = game.getTurnNum(); - controller.revealCards(sourcePermanent.getName(), new CardsImpl(card), game); + controller.revealCards(sourcePermanent.getIdName(), new CardsImpl(card), game); this.getTargets().clear(); this.getEffects().clear(); if (card.getCardType().contains(CardType.LAND)) { @@ -140,3 +145,53 @@ class KeranosGodOfStormsTriggeredAbility extends TriggeredAbilityImpl { return "Reveal the first card you draw on each of your turns. Whenever you reveal a land card this way, draw a card. Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player."; } } + +class CardsDrawnDuringTurnWatcher extends Watcher { + + private final Map amountOfCardsDrawnThisTurn = new HashMap<>(); + + public CardsDrawnDuringTurnWatcher() { + super("CardsDrawnDuringTurn", WatcherScope.GAME); + } + + public CardsDrawnDuringTurnWatcher(final CardsDrawnDuringTurnWatcher watcher) { + super(watcher); + for (Entry entry : watcher.amountOfCardsDrawnThisTurn.entrySet()) { + amountOfCardsDrawnThisTurn.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DREW_CARD) { + UUID playerId = event.getPlayerId(); + if (playerId != null) { + Integer amount = amountOfCardsDrawnThisTurn.get(playerId); + if (amount == null) { + amount = 1; + } else { + amount++; + } + amountOfCardsDrawnThisTurn.put(playerId, amount); + } + } + } + + public int getAmountCardsDrawn(UUID playerId) { + Integer amount = amountOfCardsDrawnThisTurn.get(playerId); + if (amount != null) { + return amount; + } + return 0; + } + + @Override + public void reset() { + amountOfCardsDrawnThisTurn.clear(); + } + + @Override + public CardsDrawnDuringTurnWatcher copy() { + return new CardsDrawnDuringTurnWatcher(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/GhostfireBlade.java b/Mage.Sets/src/mage/sets/khansoftarkir/GhostfireBlade.java index 9d62871250a..2b01c9e92ac 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/GhostfireBlade.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/GhostfireBlade.java @@ -67,7 +67,7 @@ public class GhostfireBlade extends CardImpl { public void adjustCosts(Ability ability, Game game) { if (ability instanceof EquipAbility) { Permanent targetCreature = game.getPermanent(ability.getTargets().getFirstTarget()); - if (targetCreature != null && targetCreature.getColor().isColorless()) { + if (targetCreature != null && targetCreature.getColor(game).isColorless()) { CardUtil.reduceCost(ability, 2); } } diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/NarsetEnlightenedMaster.java b/Mage.Sets/src/mage/sets/khansoftarkir/NarsetEnlightenedMaster.java index f67946340fd..cd3ee2eee14 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/NarsetEnlightenedMaster.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/NarsetEnlightenedMaster.java @@ -27,6 +27,7 @@ */ package mage.sets.khansoftarkir; +import java.util.List; import java.util.UUID; import mage.MageInt; import mage.MageObject; @@ -101,18 +102,16 @@ class NarsetEnlightenedMasterExileEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); if (player != null && sourceObject != null) { - for (int i = 0; i < 4; i++) { - if (player.getLibrary().size() > 0) { - Card card = player.getLibrary().getFromTop(game); - if (card != null) { - player.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source), sourceObject.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true); - if (!card.getCardType().contains(CardType.CREATURE) && !card.getCardType().contains(CardType.LAND)) { - ContinuousEffect effect = new NarsetEnlightenedMasterCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); - game.addEffect(effect, source); - } - } - } + List cards = player.getLibrary().getTopCards(game, 4); + player.moveCards(cards, Zone.LIBRARY, Zone.EXILED, source, game); + for (Card card : cards) { + if (game.getState().getZone(card.getId()) == Zone.EXILED && + !card.getCardType().contains(CardType.CREATURE) && + !card.getCardType().contains(CardType.LAND)) { + ContinuousEffect effect = new NarsetEnlightenedMasterCastFromExileEffect(); + effect.setTargetPointer(new FixedTarget(card.getId())); + game.addEffect(effect, source); + } } return true; } @@ -150,10 +149,12 @@ class NarsetEnlightenedMasterCastFromExileEffect extends AsThoughEffectImpl { public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { if (objectId.equals(getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) { Card card = game.getCard(objectId); - if (card != null && game.getState().getZone(objectId) == Zone.EXILED) { + if (card != null) { Player player = game.getPlayer(affectedControllerId); - player.setCastSourceIdWithAlternateMana(objectId, null); - return true; + if (player != null) { + player.setCastSourceIdWithAlternateMana(objectId, null); + return true; + } } } return false; diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/SarkhanTheDragonspeaker.java b/Mage.Sets/src/mage/sets/khansoftarkir/SarkhanTheDragonspeaker.java index f8aea6b73bc..cb84a4e3e85 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/SarkhanTheDragonspeaker.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/SarkhanTheDragonspeaker.java @@ -135,7 +135,7 @@ class SarkhanTheDragonspeakerEffect extends ContinuousEffectImpl { } break; case ColorChangingEffects_5: - permanent.getColor().setColor(ObjectColor.RED); + permanent.getColor(game).setColor(ObjectColor.RED); break; case AbilityAddingRemovingEffects_6: if (sublayer == SubLayer.NA) { diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/SultaiCharm.java b/Mage.Sets/src/mage/sets/khansoftarkir/SultaiCharm.java index c81bb8869c1..68e6d6f4450 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/SultaiCharm.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/SultaiCharm.java @@ -57,7 +57,6 @@ public class SultaiCharm extends CardImpl { super(ownerId, 204, "Sultai Charm", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{B}{G}{U}"); this.expansionSetCode = "KTK"; - // Choose one - // * Destroy target monocolored creature. this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); @@ -73,7 +72,6 @@ public class SultaiCharm extends CardImpl { mode = new Mode(); mode.getEffects().add(new DrawDiscardControllerEffect(2,1)); this.getSpellAbility().addMode(mode); - } public SultaiCharm(final SultaiCharm card) { diff --git a/Mage.Sets/src/mage/sets/legends/Abomination.java b/Mage.Sets/src/mage/sets/legends/Abomination.java index 8ccb34a1237..0c33a7c7818 100644 --- a/Mage.Sets/src/mage/sets/legends/Abomination.java +++ b/Mage.Sets/src/mage/sets/legends/Abomination.java @@ -95,7 +95,7 @@ class AbominationTriggeredAbility extends TriggeredAbilityImpl { Permanent blocked = game.getPermanent(event.getTargetId()); Permanent abomination = game.getPermanent(sourceId); if (blocker != null && blocker != abomination - && blocker.getColor().isWhite() + && blocker.getColor(game).isWhite() && blocked == abomination) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getSourceId())); @@ -103,14 +103,14 @@ class AbominationTriggeredAbility extends TriggeredAbilityImpl { } } if (blocker != null && blocker == abomination - && game.getPermanent(event.getTargetId()).getColor().isWhite()) { + && game.getPermanent(event.getTargetId()).getColor(game).isWhite()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); return true; } } if (blocker != null && blocker != abomination - && blocker.getColor().isGreen() + && blocker.getColor(game).isGreen() && blocked == abomination) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getSourceId())); @@ -118,7 +118,7 @@ class AbominationTriggeredAbility extends TriggeredAbilityImpl { } } if (blocker != null && blocker == abomination - && game.getPermanent(event.getTargetId()).getColor().isGreen()) { + && game.getPermanent(event.getTargetId()).getColor(game).isGreen()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); return true; diff --git a/Mage.Sets/src/mage/sets/legends/AkronLegionnaire.java b/Mage.Sets/src/mage/sets/legends/AkronLegionnaire.java index 2a9d35dc5c8..307b1ff9954 100644 --- a/Mage.Sets/src/mage/sets/legends/AkronLegionnaire.java +++ b/Mage.Sets/src/mage/sets/legends/AkronLegionnaire.java @@ -30,8 +30,8 @@ package mage.sets.legends; import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.combat.CantAttackAllAnyPlayerEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; @@ -39,9 +39,10 @@ import mage.constants.Rarity; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicate; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.permanent.ControllerPredicate; -import mage.game.Game; /** * @@ -53,7 +54,8 @@ public class AkronLegionnaire extends CardImpl { static { filter.add(new ControllerPredicate(TargetController.YOU)); - filter.add(new AkronLegionairePredicate()); + filter.add(Predicates.not(new NamePredicate("Akron Legionnaire"))); + filter.add(Predicates.not(new CardTypePredicate(CardType.ARTIFACT))); } public AkronLegionnaire(UUID ownerId) { @@ -65,7 +67,9 @@ public class AkronLegionnaire extends CardImpl { this.toughness = new MageInt(4); // Except for creatures named Akron Legionnaire and artifact creatures, creatures you control can't attack. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackAllAnyPlayerEffect(Duration.WhileOnBattlefield, filter))); + Effect effect = new CantAttackAllAnyPlayerEffect(Duration.WhileOnBattlefield, filter); + effect.setText("Except for creatures named Akron Legionnaire and artifact creatures, creatures you control can't attack"); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } @@ -78,20 +82,3 @@ public class AkronLegionnaire extends CardImpl { return new AkronLegionnaire(this); } } - -class AkronLegionairePredicate implements Predicate { - - public AkronLegionairePredicate() { - } - - @Override - public boolean apply(Card input, Game game) { - return !input.getCardType().contains(CardType.ARTIFACT) - || !input.getName().contains("Akron Legionaire"); - } - - @Override - public String toString() { - return ""; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/limitedalpha/CrystalRod.java b/Mage.Sets/src/mage/sets/limitedalpha/CrystalRod.java index fb43ebc33d1..3b0b24aff4a 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/CrystalRod.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/CrystalRod.java @@ -86,7 +86,7 @@ class CrystalRodAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().contains(ObjectColor.BLUE)) { + if (spell != null && spell.getColor(game).contains(ObjectColor.BLUE)) { return true; } } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/Forcefield.java b/Mage.Sets/src/mage/sets/limitedalpha/Forcefield.java new file mode 100644 index 00000000000..9e04886c363 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedalpha/Forcefield.java @@ -0,0 +1,52 @@ +/* + * 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.sets.limitedalpha; + +import java.util.UUID; + +/** + * + * @author emerald000 + */ +public class Forcefield extends mage.sets.mastersedition.Forcefield { + + public Forcefield(UUID ownerId) { + super(ownerId); + this.cardNumber = 243; + this.expansionSetCode = "LEA"; + } + + public Forcefield(final Forcefield card) { + super(card); + } + + @Override + public Forcefield copy() { + return new Forcefield(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedalpha/IronStar.java b/Mage.Sets/src/mage/sets/limitedalpha/IronStar.java index 24c38dacdff..0ff69ced1f0 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/IronStar.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/IronStar.java @@ -86,7 +86,7 @@ class IronStarAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().contains(ObjectColor.RED)) { + if (spell != null && spell.getColor(game).contains(ObjectColor.RED)) { return true; } } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/IvoryCup.java b/Mage.Sets/src/mage/sets/limitedalpha/IvoryCup.java index 03ce902aa2e..cfde122dc89 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/IvoryCup.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/IvoryCup.java @@ -85,7 +85,7 @@ class IvoryCupAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().contains(ObjectColor.WHITE)) { + if (spell != null && spell.getColor(game).contains(ObjectColor.WHITE)) { return true; } } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/ThroneOfBone.java b/Mage.Sets/src/mage/sets/limitedalpha/ThroneOfBone.java index 57cb3044207..529bd128f8d 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/ThroneOfBone.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/ThroneOfBone.java @@ -86,7 +86,7 @@ class ThroneOfBoneAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().contains(ObjectColor.BLACK)) { + if (spell != null && spell.getColor(game).contains(ObjectColor.BLACK)) { return true; } } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/WallOfBrambles.java b/Mage.Sets/src/mage/sets/limitedalpha/WallOfBrambles.java new file mode 100644 index 00000000000..68fa926717b --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedalpha/WallOfBrambles.java @@ -0,0 +1,69 @@ +/* + * 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.sets.limitedalpha; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class WallOfBrambles extends CardImpl { + + public WallOfBrambles(UUID ownerId) { + super(ownerId, 132, "Wall of Brambles", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{G}"); + this.expansionSetCode = "LEA"; + this.subtype.add("Plant"); + this.subtype.add("Wall"); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + // {G}: Regenerate Wall of Brambles. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{G}"))); + } + + public WallOfBrambles(final WallOfBrambles card) { + super(card); + } + + @Override + public WallOfBrambles copy() { + return new WallOfBrambles(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedalpha/WoodenSphere.java b/Mage.Sets/src/mage/sets/limitedalpha/WoodenSphere.java index 54c93977a9c..f342a150cc6 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/WoodenSphere.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/WoodenSphere.java @@ -86,7 +86,7 @@ class WoodenSphereAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().contains(ObjectColor.GREEN)) { + if (spell != null && spell.getColor(game).contains(ObjectColor.GREEN)) { return true; } } diff --git a/Mage.Sets/src/mage/sets/limitedbeta/Forcefield.java b/Mage.Sets/src/mage/sets/limitedbeta/Forcefield.java new file mode 100644 index 00000000000..5ed2b347522 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedbeta/Forcefield.java @@ -0,0 +1,52 @@ +/* + * 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.sets.limitedbeta; + +import java.util.UUID; + +/** + * + * @author emerald000 + */ +public class Forcefield extends mage.sets.mastersedition.Forcefield { + + public Forcefield(UUID ownerId) { + super(ownerId); + this.cardNumber = 245; + this.expansionSetCode = "LEB"; + } + + public Forcefield(final Forcefield card) { + super(card); + } + + @Override + public Forcefield copy() { + return new Forcefield(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedbeta/Fork.java b/Mage.Sets/src/mage/sets/limitedbeta/Fork.java index 4db929c4711..534f9d774c7 100644 --- a/Mage.Sets/src/mage/sets/limitedbeta/Fork.java +++ b/Mage.Sets/src/mage/sets/limitedbeta/Fork.java @@ -93,7 +93,7 @@ class ForkEffect extends OneShotEffect { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); if (spell != null) { Spell copy = spell.copySpell(); - copy.getColor().setRed(true); + copy.getColor(game).setRed(true); copy.setControllerId(controller.getId()); copy.setCopiedSpell(true); game.getStack().push(copy); diff --git a/Mage.Sets/src/mage/sets/limitedbeta/WallOfBrambles.java b/Mage.Sets/src/mage/sets/limitedbeta/WallOfBrambles.java new file mode 100644 index 00000000000..0a677b85597 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedbeta/WallOfBrambles.java @@ -0,0 +1,52 @@ +/* + * 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.sets.limitedbeta; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class WallOfBrambles extends mage.sets.limitedalpha.WallOfBrambles { + + public WallOfBrambles(UUID ownerId) { + super(ownerId); + this.cardNumber = 132; + this.expansionSetCode = "LEB"; + } + + public WallOfBrambles(final WallOfBrambles card) { + super(card); + } + + @Override + public WallOfBrambles copy() { + return new WallOfBrambles(this); + } +} diff --git a/Mage.Sets/src/mage/sets/magic2010/DreadWarlock.java b/Mage.Sets/src/mage/sets/magic2010/DreadWarlock.java index 2ee1ab461ab..4d6639a4fc5 100644 --- a/Mage.Sets/src/mage/sets/magic2010/DreadWarlock.java +++ b/Mage.Sets/src/mage/sets/magic2010/DreadWarlock.java @@ -114,7 +114,7 @@ class DreadWarlockEffect extends RestrictionEffect { @Override public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game) { - if (blocker.getColor().isBlack()) { + if (blocker.getColor(game).isBlack()) { return true; } return false; diff --git a/Mage.Sets/src/mage/sets/magic2010/RiseFromTheGrave.java b/Mage.Sets/src/mage/sets/magic2010/RiseFromTheGrave.java index 5596fd7914c..7a6ca7f3494 100644 --- a/Mage.Sets/src/mage/sets/magic2010/RiseFromTheGrave.java +++ b/Mage.Sets/src/mage/sets/magic2010/RiseFromTheGrave.java @@ -98,7 +98,7 @@ class RiseFromTheGraveEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - creature.getColor().setBlack(true); + creature.getColor(game).setBlack(true); } break; } diff --git a/Mage.Sets/src/mage/sets/magic2010/Silence.java b/Mage.Sets/src/mage/sets/magic2010/Silence.java index 4e406fbe936..4637cd9f97e 100644 --- a/Mage.Sets/src/mage/sets/magic2010/Silence.java +++ b/Mage.Sets/src/mage/sets/magic2010/Silence.java @@ -51,6 +51,7 @@ public class Silence extends CardImpl { super(ownerId, 31, "Silence", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{W}"); this.expansionSetCode = "M10"; + // Your opponents can't cast spells this turn. (Spells cast before this resolves are unaffected.) this.getSpellAbility().addEffect(new SilenceEffect()); } @@ -68,7 +69,7 @@ class SilenceEffect extends ContinuousRuleModifyingEffectImpl { public SilenceEffect() { super(Duration.EndOfTurn, Outcome.Benefit); - staticText = "Your opponents can't cast spells this turn"; + staticText = "Your opponents can't cast spells this turn. (Spells cast before this resolves are unaffected.)"; } public SilenceEffect(final SilenceEffect effect) { @@ -94,12 +95,14 @@ class SilenceEffect extends ContinuousRuleModifyingEffectImpl { return null; } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.CAST_SPELL; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == EventType.CAST_SPELL && game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - return true; - } - return false; + return game.getOpponents(source.getControllerId()).contains(event.getPlayerId()); } } diff --git a/Mage.Sets/src/mage/sets/magic2010/SolemnOffering.java b/Mage.Sets/src/mage/sets/magic2010/SolemnOffering.java index f3d3f8deb3b..2c695654c36 100644 --- a/Mage.Sets/src/mage/sets/magic2010/SolemnOffering.java +++ b/Mage.Sets/src/mage/sets/magic2010/SolemnOffering.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.magic2010; import java.util.UUID; @@ -34,9 +33,7 @@ import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -45,19 +42,12 @@ import mage.target.TargetPermanent; */ public class SolemnOffering extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public SolemnOffering(UUID ownerId) { super(ownerId, 33, "Solemn Offering", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{2}{W}"); this.expansionSetCode = "M10"; - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + // Destroy target artifact or enchantment. You gain 4 life. + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new GainLifeEffect(4)); } diff --git a/Mage.Sets/src/mage/sets/magic2010/VampireNocturnus.java b/Mage.Sets/src/mage/sets/magic2010/VampireNocturnus.java index 48e1bf123e7..526e6fe4405 100644 --- a/Mage.Sets/src/mage/sets/magic2010/VampireNocturnus.java +++ b/Mage.Sets/src/mage/sets/magic2010/VampireNocturnus.java @@ -126,7 +126,7 @@ class VampireNocturnusCondition implements Condition { if (player != null) { Card card = player.getLibrary().getFromTop(game); if (card != null) { - return card.getColor().isBlack(); + return card.getColor(game).isBlack(); } } return false; diff --git a/Mage.Sets/src/mage/sets/magic2011/FireServant.java b/Mage.Sets/src/mage/sets/magic2011/FireServant.java index 3ce806c9eeb..20458e7d2de 100644 --- a/Mage.Sets/src/mage/sets/magic2011/FireServant.java +++ b/Mage.Sets/src/mage/sets/magic2011/FireServant.java @@ -100,7 +100,7 @@ class FireServantEffect extends ReplacementEffectImpl { StackObject spell = game.getStack().getStackObject(event.getSourceId()); return spell != null && spell.getControllerId().equals(source.getControllerId()) && - spell.getColor().isRed() && + spell.getColor(game).isRed() && (spell.getCardType().contains(CardType.INSTANT) || spell.getCardType().contains(CardType.SORCERY)); } diff --git a/Mage.Sets/src/mage/sets/magic2011/ManicVandal.java b/Mage.Sets/src/mage/sets/magic2011/ManicVandal.java index b4974d67244..c00248da50d 100644 --- a/Mage.Sets/src/mage/sets/magic2011/ManicVandal.java +++ b/Mage.Sets/src/mage/sets/magic2011/ManicVandal.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.magic2011; import java.util.UUID; @@ -36,8 +35,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.Target; import mage.target.TargetPermanent; @@ -47,12 +45,6 @@ import mage.target.TargetPermanent; */ public class ManicVandal extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public ManicVandal(UUID ownerId) { super(ownerId, 151, "Manic Vandal", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{R}"); this.expansionSetCode = "M11"; @@ -63,7 +55,7 @@ public class ManicVandal extends CardImpl { this.toughness = new MageInt(2); Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - Target target = new TargetPermanent(filter); + Target target = new TargetPermanent(new FilterArtifactPermanent()); ability.addTarget(target); this.addAbility(ability); } @@ -76,5 +68,4 @@ public class ManicVandal extends CardImpl { public ManicVandal copy() { return new ManicVandal(this); } - } diff --git a/Mage.Sets/src/mage/sets/magic2012/ChandrasPhoenix.java b/Mage.Sets/src/mage/sets/magic2012/ChandrasPhoenix.java index 39fde9af860..27c7871fafe 100644 --- a/Mage.Sets/src/mage/sets/magic2012/ChandrasPhoenix.java +++ b/Mage.Sets/src/mage/sets/magic2012/ChandrasPhoenix.java @@ -91,7 +91,7 @@ class ChandrasPhoenixTriggeredAbility extends TriggeredAbilityImpl { if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) { Card c = game.getCard(event.getSourceId()); if (c != null) { - if (c.getColor().isRed() && (c.getCardType().contains(CardType.PLANESWALKER) || c.getCardType().contains(CardType.INSTANT) || c.getCardType().contains(CardType.SORCERY))) { + if (c.getColor(game).isRed() && (c.getCardType().contains(CardType.PLANESWALKER) || c.getCardType().contains(CardType.INSTANT) || c.getCardType().contains(CardType.SORCERY))) { return true; } } diff --git a/Mage.Sets/src/mage/sets/magic2012/PhantasmalImage.java b/Mage.Sets/src/mage/sets/magic2012/PhantasmalImage.java index 576bfd7005b..3fef0ff88fb 100644 --- a/Mage.Sets/src/mage/sets/magic2012/PhantasmalImage.java +++ b/Mage.Sets/src/mage/sets/magic2012/PhantasmalImage.java @@ -110,7 +110,9 @@ class PhantasmalImageCopyEffect extends OneShotEffect { if (!permanent.getSubtype().contains("Illusion")) { permanent.getSubtype().add("Illusion"); } - permanent.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), game); + // Add directly because the created permanent is only used to copy from, so there is no need to add the ability to e.g. TriggeredAbilities + permanent.getAbilities().add(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + //permanent.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), game); return true; } }); diff --git a/Mage.Sets/src/mage/sets/magic2013/PublicExecution.java b/Mage.Sets/src/mage/sets/magic2013/PublicExecution.java index aa8db6a0cd9..75758f1f41b 100644 --- a/Mage.Sets/src/mage/sets/magic2013/PublicExecution.java +++ b/Mage.Sets/src/mage/sets/magic2013/PublicExecution.java @@ -61,7 +61,6 @@ public class PublicExecution extends CardImpl { super(ownerId, 105, "Public Execution", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{5}{B}"); this.expansionSetCode = "M13"; - // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); diff --git a/Mage.Sets/src/mage/sets/magic2013/Smelt.java b/Mage.Sets/src/mage/sets/magic2013/Smelt.java index ff67773bb90..2db77343702 100644 --- a/Mage.Sets/src/mage/sets/magic2013/Smelt.java +++ b/Mage.Sets/src/mage/sets/magic2013/Smelt.java @@ -44,7 +44,6 @@ public class Smelt extends CardImpl { super(ownerId, 149, "Smelt", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}"); this.expansionSetCode = "M13"; - // Destroy target artifact. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetArtifactPermanent()); diff --git a/Mage.Sets/src/mage/sets/magic2014/BurningEarth.java b/Mage.Sets/src/mage/sets/magic2014/BurningEarth.java index 7c62477e23a..9f4db5a56a4 100644 --- a/Mage.Sets/src/mage/sets/magic2014/BurningEarth.java +++ b/Mage.Sets/src/mage/sets/magic2014/BurningEarth.java @@ -34,7 +34,9 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetTargetPointer; -import mage.filter.common.FilterNonlandPermanent; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SupertypePredicate; /** * @@ -42,16 +44,20 @@ import mage.filter.common.FilterNonlandPermanent; */ public class BurningEarth extends CardImpl { + private final static FilterLandPermanent filter = new FilterLandPermanent("a player taps a nonbasic land"); + + static { + filter.add(Predicates.not(new SupertypePredicate("Basic"))); + } + public BurningEarth(UUID ownerId) { super(ownerId, 130, "Burning Earth", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}"); this.expansionSetCode = "M14"; - // Whenever a player taps a nonbasic land for mana, Burning Earth deals 1 damage to that player. this.addAbility(new TapForManaAllTriggeredAbility( new DamageTargetEffect(1, true, "that player"), - new FilterNonlandPermanent("a player taps a nonbasic land"), - SetTargetPointer.PLAYER)); + filter, SetTargetPointer.PLAYER)); } public BurningEarth(final BurningEarth card) { diff --git a/Mage.Sets/src/mage/sets/magic2014/FiendslayerPaladin.java b/Mage.Sets/src/mage/sets/magic2014/FiendslayerPaladin.java index 1f4efab0209..b20cc8df831 100644 --- a/Mage.Sets/src/mage/sets/magic2014/FiendslayerPaladin.java +++ b/Mage.Sets/src/mage/sets/magic2014/FiendslayerPaladin.java @@ -123,8 +123,8 @@ class FiendslayerPaladinEffect extends ContinuousRuleModifyingEffectImpl { Card targetCard = game.getCard(event.getTargetId()); StackObject stackObject = (StackObject) game.getStack().getStackObject(event.getSourceId()); if (targetCard != null && stackObject != null && targetCard.getId().equals(source.getSourceId())) { - if (stackObject.getColor().contains(ObjectColor.BLACK) - || stackObject.getColor().contains(ObjectColor.RED)) { + if (stackObject.getColor(game).contains(ObjectColor.BLACK) + || stackObject.getColor(game).contains(ObjectColor.RED)) { if (!stackObject.getControllerId().equals(source.getControllerId()) && stackObject.getCardType().contains(CardType.INSTANT) || stackObject.getCardType().contains(CardType.SORCERY)) { diff --git a/Mage.Sets/src/mage/sets/magic2015/AvacynGuardianAngel.java b/Mage.Sets/src/mage/sets/magic2015/AvacynGuardianAngel.java index 05e0d807513..0e6d2fa2706 100644 --- a/Mage.Sets/src/mage/sets/magic2015/AvacynGuardianAngel.java +++ b/Mage.Sets/src/mage/sets/magic2015/AvacynGuardianAngel.java @@ -127,7 +127,7 @@ class AvacynGuardianAngelPreventToCreatureEffect extends PreventionEffectImpl { && event.getTargetId().equals(getTargetPointer().getFirst(game, source))) { ChoiceColor choice = (ChoiceColor) source.getChoices().get(0); MageObject sourceObject = game.getObject(event.getSourceId()); - if (sourceObject != null && choice != null && sourceObject.getColor().shares(choice.getColor())) { + if (sourceObject != null && choice != null && sourceObject.getColor(game).shares(choice.getColor())) { return true; } } @@ -166,7 +166,7 @@ class AvacynGuardianAngelPreventToPlayerEffect extends PreventionEffectImpl { && event.getTargetId().equals(getTargetPointer().getFirst(game, source))) { ChoiceColor choice = (ChoiceColor) source.getChoices().get(0); MageObject sourceObject = game.getObject(event.getSourceId()); - if (sourceObject != null && choice != null && sourceObject.getColor().shares(choice.getColor())) { + if (sourceObject != null && choice != null && sourceObject.getColor(game).shares(choice.getColor())) { return true; } } diff --git a/Mage.Sets/src/mage/sets/magic2015/ChiefEngineer.java b/Mage.Sets/src/mage/sets/magic2015/ChiefEngineer.java index 32f86d943ac..516de3d0fd9 100644 --- a/Mage.Sets/src/mage/sets/magic2015/ChiefEngineer.java +++ b/Mage.Sets/src/mage/sets/magic2015/ChiefEngineer.java @@ -27,14 +27,12 @@ */ package mage.sets.magic2015; -import java.util.Iterator; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.keyword.ConvokeAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; @@ -43,7 +41,6 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.SubLayer; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.FilterSpell; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; diff --git a/Mage.Sets/src/mage/sets/magic2015/HushwingGryff.java b/Mage.Sets/src/mage/sets/magic2015/HushwingGryff.java index 1fe61e0b698..3cbc4aba1c6 100644 --- a/Mage.Sets/src/mage/sets/magic2015/HushwingGryff.java +++ b/Mage.Sets/src/mage/sets/magic2015/HushwingGryff.java @@ -36,13 +36,13 @@ import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; +import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -104,18 +104,18 @@ class HushwingGryffEffect extends ContinuousRuleModifyingEffectImpl { return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; } - @Override + @Override public boolean applies(GameEvent event, Ability source, Game game) { - Permanent permanent = ((EntersTheBattlefieldEvent)event).getTarget(); - if (permanent != null && permanent.getCardType().contains(CardType.CREATURE)) { - // Because replacement events have to be executed - // call replaceEvent here without calling the triggering event after - game.getContinuousEffects().replaceEvent(event, game); - return true; + Ability ability = (Ability) getValue("targetAbility"); + if (ability != null && AbilityType.TRIGGERED.equals(ability.getAbilityType())) { + Permanent p = game.getPermanent(event.getTargetId()); + if (p != null && p.getCardType().contains(CardType.CREATURE)) { + return true; + } } return false; } - + @Override public boolean apply(Game game, Ability source) { return true; @@ -126,4 +126,4 @@ class HushwingGryffEffect extends ContinuousRuleModifyingEffectImpl { return new HushwingGryffEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/magic2015/PolymorphistsJest.java b/Mage.Sets/src/mage/sets/magic2015/PolymorphistsJest.java index 44c582583e5..e48c828a987 100644 --- a/Mage.Sets/src/mage/sets/magic2015/PolymorphistsJest.java +++ b/Mage.Sets/src/mage/sets/magic2015/PolymorphistsJest.java @@ -115,12 +115,12 @@ class PolymorphistsJestEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - permanent.getColor().setBlack(false); - permanent.getColor().setGreen(false); - permanent.getColor().setBlue(false); - permanent.getColor().setWhite(false); - permanent.getColor().setBlack(false); - permanent.getColor().setColor(ObjectColor.BLUE); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setGreen(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setColor(ObjectColor.BLUE); } break; case AbilityAddingRemovingEffects_6: diff --git a/Mage.Sets/src/mage/sets/magic2015/SoulOfRavnica.java b/Mage.Sets/src/mage/sets/magic2015/SoulOfRavnica.java index f6c4344da3b..e77bbcf0fb3 100644 --- a/Mage.Sets/src/mage/sets/magic2015/SoulOfRavnica.java +++ b/Mage.Sets/src/mage/sets/magic2015/SoulOfRavnica.java @@ -103,19 +103,19 @@ class SoulOfRavnicaEffect extends OneShotEffect { if (controller != null) { HashSet colors = new HashSet<>(); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(controller.getId())) { - if (permanent.getColor().isBlack()) { + if (permanent.getColor(game).isBlack()) { colors.add(ObjectColor.BLACK); } - if (permanent.getColor().isBlue()) { + if (permanent.getColor(game).isBlue()) { colors.add(ObjectColor.BLUE); } - if (permanent.getColor().isRed()) { + if (permanent.getColor(game).isRed()) { colors.add(ObjectColor.RED); } - if (permanent.getColor().isGreen()) { + if (permanent.getColor(game).isGreen()) { colors.add(ObjectColor.GREEN); } - if (permanent.getColor().isWhite()) { + if (permanent.getColor(game).isWhite()) { colors.add(ObjectColor.WHITE); } } diff --git a/Mage.Sets/src/mage/sets/mastersedition/ArtifactBlast.java b/Mage.Sets/src/mage/sets/mastersedition/ArtifactBlast.java new file mode 100644 index 00000000000..9433ebb7cd0 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mastersedition/ArtifactBlast.java @@ -0,0 +1,52 @@ +/* + * 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.sets.mastersedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class ArtifactBlast extends mage.sets.antiquities.ArtifactBlast { + + public ArtifactBlast(UUID ownerId) { + super(ownerId); + this.cardNumber = 85; + this.expansionSetCode = "MED"; + } + + public ArtifactBlast(final ArtifactBlast card) { + super(card); + } + + @Override + public ArtifactBlast copy() { + return new ArtifactBlast(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/mastersedition/Fissure.java b/Mage.Sets/src/mage/sets/mastersedition/Fissure.java new file mode 100644 index 00000000000..1999cce4486 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mastersedition/Fissure.java @@ -0,0 +1,52 @@ +/* + * 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.sets.mastersedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class Fissure extends mage.sets.thedark.Fissure { + + public Fissure(UUID ownerId) { + super(ownerId); + this.cardNumber = 93; + this.expansionSetCode = "MED"; + } + + public Fissure(final Fissure card) { + super(card); + } + + @Override + public Fissure copy() { + return new Fissure(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mastersedition/Forcefield.java b/Mage.Sets/src/mage/sets/mastersedition/Forcefield.java new file mode 100644 index 00000000000..cc7ebcd2527 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mastersedition/Forcefield.java @@ -0,0 +1,151 @@ +/* + * 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.sets.mastersedition; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.UnblockedPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author emerald000 + */ +public class Forcefield extends CardImpl { + + public Forcefield(UUID ownerId) { + super(ownerId, 157, "Forcefield", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MED"; + + // {1}: The next time an unblocked creature of your choice would deal combat damage to you this turn, prevent all but 1 of that damage. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ForcefieldEffect(), new GenericManaCost(1))); + } + + public Forcefield(final Forcefield card) { + super(card); + } + + @Override + public Forcefield copy() { + return new Forcefield(this); + } +} + +class ForcefieldEffect extends OneShotEffect { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("an unblocked creature"); + static { + filter.add(new UnblockedPredicate()); + } + + ForcefieldEffect() { + super(Outcome.PreventDamage); + this.staticText = "The next time an unblocked creature of your choice would deal combat damage to you this turn, prevent all but 1 of that damage"; + } + + ForcefieldEffect(final ForcefieldEffect effect) { + super(effect); + } + + @Override + public ForcefieldEffect copy() { + return new ForcefieldEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Target target = new TargetCreaturePermanent(1, 1, filter, true); + if (controller.choose(Outcome.PreventDamage, target, source.getSourceId(), game)) { + ContinuousEffect effect = new ForcefieldPreventionEffect(); + effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); + game.addEffect(effect, source); + } + return true; + } + return false; + } +} + +class ForcefieldPreventionEffect extends PreventionEffectImpl { + + ForcefieldPreventionEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, true, false); + this.staticText = "Prevent all but 1 of that damage"; + } + + ForcefieldPreventionEffect(ForcefieldPreventionEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + int damage = event.getAmount(); + if (damage > 0) { + this.amountToPrevent = damage - 1; + preventDamageAction(event, source, game); + this.discard(); + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.DAMAGE_PLAYER; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return super.applies(event, source, game) + && event.getSourceId().equals(this.getTargetPointer().getFirst(game, source)); + } + + @Override + public ForcefieldPreventionEffect copy() { + return new ForcefieldPreventionEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/ArtifactBlast.java b/Mage.Sets/src/mage/sets/masterseditioniv/ArtifactBlast.java new file mode 100644 index 00000000000..ef3a5755c84 --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditioniv/ArtifactBlast.java @@ -0,0 +1,52 @@ +/* + * 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.sets.masterseditioniv; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class ArtifactBlast extends mage.sets.antiquities.ArtifactBlast { + + public ArtifactBlast(UUID ownerId) { + super(ownerId); + this.cardNumber = 108; + this.expansionSetCode = "ME4"; + } + + public ArtifactBlast(final ArtifactBlast card) { + super(card); + } + + @Override + public ArtifactBlast copy() { + return new ArtifactBlast(this); + } +} diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/ClayStatue.java b/Mage.Sets/src/mage/sets/masterseditioniv/ClayStatue.java new file mode 100644 index 00000000000..5e469e72ca6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditioniv/ClayStatue.java @@ -0,0 +1,54 @@ +/* + * 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.sets.masterseditioniv; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class ClayStatue extends mage.sets.fourthedition.ClayStatue { + + public ClayStatue(UUID ownerId) { + super(ownerId); + this.cardNumber = 189; + this.expansionSetCode = "ME4"; + this.rarity = Rarity.UNCOMMON; + } + + public ClayStatue(final ClayStatue card) { + super(card); + } + + @Override + public ClayStatue copy() { + return new ClayStatue(this); + } +} diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/FalseSummoning.java b/Mage.Sets/src/mage/sets/masterseditioniv/FalseSummoning.java new file mode 100644 index 00000000000..74c2c1a5aa5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditioniv/FalseSummoning.java @@ -0,0 +1,52 @@ +/* + * 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.sets.masterseditioniv; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class FalseSummoning extends mage.sets.portalsecondage.FalseSummoning { + + public FalseSummoning(UUID ownerId) { + super(ownerId); + this.cardNumber = 49; + this.expansionSetCode = "ME4"; + } + + public FalseSummoning(final FalseSummoning card) { + super(card); + } + + @Override + public FalseSummoning copy() { + return new FalseSummoning(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/EyeOfRamos.java b/Mage.Sets/src/mage/sets/mercadianmasques/EyeOfRamos.java new file mode 100644 index 00000000000..91c629d22d1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mercadianmasques/EyeOfRamos.java @@ -0,0 +1,65 @@ +/* + * 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.sets.mercadianmasques; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class EyeOfRamos extends CardImpl { + + public EyeOfRamos(UUID ownerId) { + super(ownerId, 294, "Eye of Ramos", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MMQ"; + + // {tap}: Add {U} to your mana pool. + this.addAbility(new BlueManaAbility()); + + // Sacrifice Eye of Ramos: Add {U} to your mana pool. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlueMana, new SacrificeSourceCost())); + } + + public EyeOfRamos(final EyeOfRamos card) { + super(card); + } + + @Override + public EyeOfRamos copy() { + return new EyeOfRamos(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/HeartOfRamos.java b/Mage.Sets/src/mage/sets/mercadianmasques/HeartOfRamos.java new file mode 100644 index 00000000000..d87eaaef7b8 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mercadianmasques/HeartOfRamos.java @@ -0,0 +1,65 @@ +/* + * 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.sets.mercadianmasques; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class HeartOfRamos extends CardImpl { + + public HeartOfRamos(UUID ownerId) { + super(ownerId, 296, "Heart of Ramos", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MMQ"; + + // {tap}: Add {R} to your mana pool. + this.addAbility(new RedManaAbility()); + + // Sacrifice Heart of Ramos: Add {R} to your mana pool. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.RedMana, new SacrificeSourceCost())); + } + + public HeartOfRamos(final HeartOfRamos card) { + super(card); + } + + @Override + public HeartOfRamos copy() { + return new HeartOfRamos(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/HornOfRamos.java b/Mage.Sets/src/mage/sets/mercadianmasques/HornOfRamos.java new file mode 100644 index 00000000000..c7c3072a846 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mercadianmasques/HornOfRamos.java @@ -0,0 +1,65 @@ +/* + * 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.sets.mercadianmasques; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class HornOfRamos extends CardImpl { + + public HornOfRamos(UUID ownerId) { + super(ownerId, 299, "Horn of Ramos", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MMQ"; + + // {tap}: Add {G} to your mana pool. + this.addAbility(new GreenManaAbility()); + + // Sacrifice Horn of Ramos: Add {G} to your mana pool. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.GreenMana, new SacrificeSourceCost())); + } + + public HornOfRamos(final HornOfRamos card) { + super(card); + } + + @Override + public HornOfRamos copy() { + return new HornOfRamos(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/Misdirection.java b/Mage.Sets/src/mage/sets/mercadianmasques/Misdirection.java index 092c9a97f51..094470b2e15 100644 --- a/Mage.Sets/src/mage/sets/mercadianmasques/Misdirection.java +++ b/Mage.Sets/src/mage/sets/mercadianmasques/Misdirection.java @@ -60,10 +60,10 @@ public class Misdirection extends CardImpl { super(ownerId, 87, "Misdirection", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); this.expansionSetCode = "MMQ"; - // You may exile a blue card from your hand rather than pay Misdirection's mana cost. FilterOwnedCard filterCardInHand = new FilterOwnedCard("a blue card from your hand"); filterCardInHand.add(new ColorPredicate(ObjectColor.BLUE)); + // the exile cost can never be paid with the card itself filterCardInHand.add(Predicates.not(new CardIdPredicate(this.getId()))); this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filterCardInHand)))); diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/SkullOfRamos.java b/Mage.Sets/src/mage/sets/mercadianmasques/SkullOfRamos.java new file mode 100644 index 00000000000..ca888a25691 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mercadianmasques/SkullOfRamos.java @@ -0,0 +1,65 @@ +/* + * 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.sets.mercadianmasques; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class SkullOfRamos extends CardImpl { + + public SkullOfRamos(UUID ownerId) { + super(ownerId, 312, "Skull of Ramos", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MMQ"; + + // {tap}: Add {B} to your mana pool. + this.addAbility(new BlackManaAbility()); + + // Sacrifice Skull of Ramos: Add {B} to your mana pool. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlackMana, new SacrificeSourceCost())); + } + + public SkullOfRamos(final SkullOfRamos card) { + super(card); + } + + @Override + public SkullOfRamos copy() { + return new SkullOfRamos(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/ToothOfRamos.java b/Mage.Sets/src/mage/sets/mercadianmasques/ToothOfRamos.java new file mode 100644 index 00000000000..6017ba27e36 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mercadianmasques/ToothOfRamos.java @@ -0,0 +1,65 @@ +/* + * 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.sets.mercadianmasques; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class ToothOfRamos extends CardImpl { + + public ToothOfRamos(UUID ownerId) { + super(ownerId, 313, "Tooth of Ramos", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MMQ"; + + // {tap}: Add {W} to your mana pool. + this.addAbility(new WhiteManaAbility()); + + // Sacrifice Tooth of Ramos: Add {W} to your mana pool. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.WhiteMana, new SacrificeSourceCost())); + } + + public ToothOfRamos(final ToothOfRamos card) { + super(card); + } + + @Override + public ToothOfRamos copy() { + return new ToothOfRamos(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mirrodin/ChromeMox.java b/Mage.Sets/src/mage/sets/mirrodin/ChromeMox.java index dab4e360d89..00e8256abb9 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/ChromeMox.java +++ b/Mage.Sets/src/mage/sets/mirrodin/ChromeMox.java @@ -151,7 +151,7 @@ class ChromeMoxManaEffect extends ManaEffect { if (imprintedCard != null) { Choice choice = new ChoiceImpl(true); choice.setMessage("Pick a mana color"); - ObjectColor color = imprintedCard.getColor(); + ObjectColor color = imprintedCard.getColor(game); if (color.isBlack()) { choice.getChoices().add("Black"); } diff --git a/Mage.Sets/src/mage/sets/mirrodin/CrystalShard.java b/Mage.Sets/src/mage/sets/mirrodin/CrystalShard.java index b5d00bc2846..03a2101ae59 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/CrystalShard.java +++ b/Mage.Sets/src/mage/sets/mirrodin/CrystalShard.java @@ -98,19 +98,23 @@ class CrystalShardEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent targetCreature = game.getPermanent(source.getFirstTarget()); - if (targetCreature != null) { - Player player = game.getPlayer(targetCreature.getControllerId()); - if (player != null) { - cost.clearPaid(); - final StringBuilder sb = new StringBuilder("Pay {1} otherwise ").append(targetCreature.getName()).append(" will be returned to its owner's hand)"); - if (player.chooseUse(Outcome.Benefit, sb.toString(), game)) { - cost.pay(source, game, targetCreature.getControllerId(), targetCreature.getControllerId(), false); - } - if (!cost.isPaid()) { - return targetCreature.moveToZone(Zone.HAND, source.getSourceId(), game, true); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (targetCreature != null) { + Player player = game.getPlayer(targetCreature.getControllerId()); + if (player != null) { + cost.clearPaid(); + final StringBuilder sb = new StringBuilder("Pay {1}? (Otherwise ").append(targetCreature.getName()).append(" will be returned to its owner's hand)"); + if (player.chooseUse(Outcome.Benefit, sb.toString(), game)) { + cost.pay(source, game, targetCreature.getControllerId(), targetCreature.getControllerId(), false); + } + if (!cost.isPaid()) { + controller.moveCards(targetCreature, Zone.BATTLEFIELD, Zone.HAND, source, game); + } } } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/mirrodin/Shatter.java b/Mage.Sets/src/mage/sets/mirrodin/Shatter.java index 6ee15fcc26d..6c8c5d51543 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/Shatter.java +++ b/Mage.Sets/src/mage/sets/mirrodin/Shatter.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.mirrodin; import java.util.UUID; @@ -33,8 +32,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -42,18 +40,13 @@ import mage.target.TargetPermanent; * @author Loki */ public class Shatter extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Shatter (UUID ownerId) { super(ownerId, 105, "Shatter", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{R}"); this.expansionSetCode = "MRD"; + // Destroy target artifact. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); } public Shatter (final Shatter card) { @@ -64,5 +57,4 @@ public class Shatter extends CardImpl { public Shatter copy() { return new Shatter(this); } - } diff --git a/Mage.Sets/src/mage/sets/mirrodin/VedalkenArchmage.java b/Mage.Sets/src/mage/sets/mirrodin/VedalkenArchmage.java index d4eb18b145b..5589557fd24 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/VedalkenArchmage.java +++ b/Mage.Sets/src/mage/sets/mirrodin/VedalkenArchmage.java @@ -34,22 +34,13 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterSpell; -import mage.filter.common.FilterArtifactCard; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; /** * * @author LevelX2 */ public class VedalkenArchmage extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public VedalkenArchmage(UUID ownerId) { super(ownerId, 55, "Vedalken Archmage", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); this.expansionSetCode = "MRD"; @@ -60,6 +51,7 @@ public class VedalkenArchmage extends CardImpl { this.toughness = new MageInt(2); // Whenever you cast an artifact spell, draw a card. + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); this.addAbility(new SpellCastControllerTriggeredAbility(new DrawCardSourceControllerEffect(1), filter, false)); } diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/MirranSpy.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/MirranSpy.java index 8fb52da3e43..e2c07fb8cd4 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/MirranSpy.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/MirranSpy.java @@ -35,7 +35,7 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterSpell; +import mage.filter.common.FilterArtifactSpell; import mage.target.common.TargetCreaturePermanent; /** @@ -43,9 +43,7 @@ import mage.target.common.TargetCreaturePermanent; * @author North */ public class MirranSpy extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - + public MirranSpy(UUID ownerId) { super(ownerId, 26, "Mirran Spy", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.expansionSetCode = "MBS"; @@ -58,6 +56,7 @@ public class MirranSpy extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever you cast an artifact spell, you may untap target creature. + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); SpellCastControllerTriggeredAbility ability = new SpellCastControllerTriggeredAbility(new UntapTargetEffect(), filter, true); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/SteelSabotage.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/SteelSabotage.java index 701966d1efa..f43bfe117fe 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/SteelSabotage.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/SteelSabotage.java @@ -34,31 +34,22 @@ import mage.abilities.Mode; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; import mage.target.TargetSpell; import mage.target.common.TargetArtifactPermanent; +import mage.filter.common.FilterArtifactSpell; /** * * @author North */ public class SteelSabotage extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("artifact spell"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public SteelSabotage(UUID ownerId) { super(ownerId, 33, "Steel Sabotage", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{U}"); this.expansionSetCode = "MBS"; - // Choose one - Counter target artifact spell; or return target artifact to its owner's hand. this.getSpellAbility().addEffect(new CounterTargetEffect()); - this.getSpellAbility().addTarget(new TargetSpell(filter)); + this.getSpellAbility().addTarget(new TargetSpell(new FilterArtifactSpell())); Mode mode = new Mode(); mode.getEffects().add(new ReturnToHandTargetEffect()); mode.getTargets().add(new TargetArtifactPermanent()); diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/TitanForge.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/TitanForge.java index 93fba1c2065..d748d3754ee 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/TitanForge.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/TitanForge.java @@ -58,7 +58,7 @@ public class TitanForge extends CardImpl { ability.addCost(new TapSourceCost()); this.addAbility(ability); - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new GolemToken()), new TapSourceCost()); + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new TitanForgeGolemToken()), new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(3))); this.addAbility(ability); @@ -75,8 +75,8 @@ public class TitanForge extends CardImpl { } -class GolemToken extends Token { - GolemToken() { +class TitanForgeGolemToken extends Token { + TitanForgeGolemToken() { super("Golem", "a 9/9 colorless Golem artifact creature token"); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/ViridianCorrupter.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/ViridianCorrupter.java index 93d5b5a96b7..1b78b3a011c 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/ViridianCorrupter.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/ViridianCorrupter.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.mirrodinbesieged; import java.util.UUID; @@ -37,8 +36,7 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.InfectAbility; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.Target; import mage.target.TargetPermanent; @@ -48,12 +46,6 @@ import mage.target.TargetPermanent; */ public class ViridianCorrupter extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public ViridianCorrupter (UUID ownerId) { super(ownerId, 94, "Viridian Corrupter", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); this.expansionSetCode = "MBS"; @@ -64,8 +56,9 @@ public class ViridianCorrupter extends CardImpl { this.toughness = new MageInt(2); this.addAbility(InfectAbility.getInstance()); + // When Viridian Corrupter enters the battlefield, destroy target artifact. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - Target target = new TargetPermanent(filter); + Target target = new TargetPermanent(new FilterArtifactPermanent()); ability.addTarget(target); this.addAbility(ability); } @@ -78,5 +71,4 @@ public class ViridianCorrupter extends CardImpl { public ViridianCorrupter copy() { return new ViridianCorrupter(this); } - } diff --git a/Mage.Sets/src/mage/sets/modernmasters2015/KarplusanStrider.java b/Mage.Sets/src/mage/sets/modernmasters2015/KarplusanStrider.java index cedb57ab93d..81ceb5b06eb 100644 --- a/Mage.Sets/src/mage/sets/modernmasters2015/KarplusanStrider.java +++ b/Mage.Sets/src/mage/sets/modernmasters2015/KarplusanStrider.java @@ -111,7 +111,7 @@ class KarplusanStriderEffect extends ContinuousRuleModifyingEffectImpl { Permanent targettedPermanent = game.getPermanent(event.getTargetId()); Spell sourceSpell = game.getStack().getSpell(event.getSourceId()); if (targettedPermanent != null && sourceSpell != null) { - return sourceSpell.getColor().isBlue() || sourceSpell.getColor().isBlack(); + return sourceSpell.getColor(game).isBlue() || sourceSpell.getColor(game).isBlack(); } } return false; diff --git a/Mage.Sets/src/mage/sets/nemesis/SealOfCleansing.java b/Mage.Sets/src/mage/sets/nemesis/SealOfCleansing.java index b4ba95f23ff..19f87fb040b 100644 --- a/Mage.Sets/src/mage/sets/nemesis/SealOfCleansing.java +++ b/Mage.Sets/src/mage/sets/nemesis/SealOfCleansing.java @@ -28,7 +28,6 @@ package mage.sets.nemesis; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.Ability; @@ -37,9 +36,7 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -47,23 +44,14 @@ import mage.target.TargetPermanent; * @author Plopman */ public class SealOfCleansing extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public SealOfCleansing(UUID ownerId) { super(ownerId, 18, "Seal of Cleansing", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); this.expansionSetCode = "NMS"; - // Sacrifice Seal of Cleansing: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java b/Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java index a040d8b982d..79bdbcafa4f 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java @@ -34,17 +34,20 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.choices.ChoiceColorOrArtifact; +import mage.constants.Outcome; import mage.filter.FilterCard; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetControlledPermanent; /** @@ -62,10 +65,11 @@ public class ApostlesBlessing extends CardImpl { public ApostlesBlessing(UUID ownerId) { super(ownerId, 2, "Apostle's Blessing", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{WP}"); this.expansionSetCode = "NPH"; - - this.getSpellAbility().addEffect(new ApostlesBlessingEffect(Duration.EndOfTurn)); + + // ({WP} can be paid with either {W} or 2 life.) + // Target artifact or creature you control gains protection from artifacts or from the color of your choice until end of turn. + this.getSpellAbility().addEffect(new ApostlesBlessingEffect()); this.getSpellAbility().addTarget(new TargetControlledPermanent(filter)); - this.getSpellAbility().addChoice(new ChoiceColorOrArtifact()); } public ApostlesBlessing(final ApostlesBlessing card) { @@ -79,40 +83,47 @@ public class ApostlesBlessing extends CardImpl { } -class ApostlesBlessingEffect extends GainAbilityTargetEffect { - - public ApostlesBlessingEffect(Duration duration) { - super(new ProtectionAbility(new FilterCard()), duration); - staticText = "Target artifact or creature gains protection from artifacts or from the color of your choice until end of turn"; +class ApostlesBlessingEffect extends OneShotEffect { + + public ApostlesBlessingEffect() { + super(Outcome.AddAbility); + this.staticText = "Target artifact or creature gains protection from artifacts or from the color of your choice until end of turn"; } - + public ApostlesBlessingEffect(final ApostlesBlessingEffect effect) { super(effect); } - + @Override public ApostlesBlessingEffect copy() { return new ApostlesBlessingEffect(this); } - + @Override public boolean apply(Game game, Ability source) { - FilterCard protectionFilter = new FilterCard(); - ChoiceColorOrArtifact choice = (ChoiceColorOrArtifact) source.getChoices().get(0); - if (choice.isArtifactSelected()) { - protectionFilter.add(new CardTypePredicate(CardType.ARTIFACT)); - } else { - protectionFilter.add(new ColorPredicate(choice.getColor())); - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + ChoiceColorOrArtifact choice = new ChoiceColorOrArtifact(); + while (!choice.isChosen()) { + if (!controller.isInGame()) { + return false; + } + controller.choose(outcome, choice, game); + } - protectionFilter.setMessage(choice.getChoice()); - ((ProtectionAbility) ability).setFilter(protectionFilter); - Permanent creature = game.getPermanent(source.getFirstTarget()); - if (creature != null) { - creature.addAbility(ability, game); + FilterCard protectionFilter = new FilterCard(); + if (choice.isArtifactSelected()) { + protectionFilter.add(new CardTypePredicate(CardType.ARTIFACT)); + } else { + protectionFilter.add(new ColorPredicate(choice.getColor())); + } + protectionFilter.setMessage(choice.getChoice()); + ProtectionAbility protectionAbility = new ProtectionAbility(protectionFilter); + ContinuousEffect effect = new GainAbilityTargetEffect(protectionAbility, Duration.EndOfTurn); + effect.setTargetPointer(getTargetPointer()); + game.addEffect(effect, source); return true; - } + } return false; } - } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/BladeSplicer.java b/Mage.Sets/src/mage/sets/newphyrexia/BladeSplicer.java index d1ab3a10b54..ffa7e29184d 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/BladeSplicer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/BladeSplicer.java @@ -66,7 +66,11 @@ public class BladeSplicer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken()))); + + // When Blade Splicer enters the battlefield, put a 3/3 colorless Golem artifact creature token onto the battlefield. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(expansionSetCode)))); + + // Golem creatures you control have first strike. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter))); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/CagedSun.java b/Mage.Sets/src/mage/sets/newphyrexia/CagedSun.java index 67e7b0fdfc7..e7fdb969e10 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/CagedSun.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/CagedSun.java @@ -111,7 +111,7 @@ class CagedSunEffect2 extends ContinuousEffectImpl { ObjectColor color = (ObjectColor) game.getState().getValue(permanent.getId() + "_color"); if (color != null) { for (Permanent perm: game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { - if (perm.getColor().contains(color)) { + if (perm.getColor(game).contains(color)) { perm.addPower(1); perm.addToughness(1); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/ConversionChamber.java b/Mage.Sets/src/mage/sets/newphyrexia/ConversionChamber.java index 78981ee0fc6..bbe38bfc816 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/ConversionChamber.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/ConversionChamber.java @@ -62,7 +62,7 @@ public class ConversionChamber extends CardImpl { ability.addCost(new TapSourceCost()); this.addAbility(ability); // {2}, {T}, Remove a charge counter from Conversion Chamber: Put a 3/3 colorless Golem artifact creature token onto the battlefield. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new GolemToken()), new GenericManaCost(2)); + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new GolemToken(expansionSetCode)), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/newphyrexia/FallenFerromancer.java b/Mage.Sets/src/mage/sets/newphyrexia/FallenFerromancer.java index 3214d236871..1874096dd70 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/FallenFerromancer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/FallenFerromancer.java @@ -36,6 +36,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.InfectAbility; import mage.cards.CardImpl; import mage.target.common.TargetCreatureOrPlayer; @@ -53,6 +54,11 @@ public class FallenFerromancer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); + + // Infect (This creature deals damage to creatures in the form of -1/-1 counters and to players in the form of poison counters.) + this.addAbility(InfectAbility.getInstance()); + + // {1}{R}, {T}: Fallen Ferromancer deals 1 damage to target creature or player. SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new TapSourceCost()); ability.addCost(new ManaCostsImpl("{1}{R}")); ability.addTarget(new TargetCreatureOrPlayer()); diff --git a/Mage.Sets/src/mage/sets/newphyrexia/MasterSplicer.java b/Mage.Sets/src/mage/sets/newphyrexia/MasterSplicer.java index 25d074d14d6..d59171cda89 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/MasterSplicer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/MasterSplicer.java @@ -63,7 +63,7 @@ public class MasterSplicer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken()))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(expansionSetCode)))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, false))); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/MaulSplicer.java b/Mage.Sets/src/mage/sets/newphyrexia/MaulSplicer.java index 032799a9258..e928b5e74f9 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/MaulSplicer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/MaulSplicer.java @@ -66,7 +66,10 @@ public class MaulSplicer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(), 2))); + // When Maul Splicer enters the battlefield, put two 3/3 colorless Golem artifact creature tokens onto the battlefield. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(expansionSetCode), 2))); + + // Golem creatures you control have trample. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter))); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java b/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java index eebfcb13119..82ea8bc0814 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java @@ -59,11 +59,9 @@ public class PraetorsGrasp extends CardImpl { super(ownerId, 71, "Praetor's Grasp", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); this.expansionSetCode = "NPH"; - // Search target opponent's library for a card and exile it face down. Then that player shuffles his or her library. You may look at and play that card for as long as it remains exiled. this.getSpellAbility().addEffect(new PraetorsGraspEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); - } public PraetorsGrasp(final PraetorsGrasp card) { @@ -105,7 +103,7 @@ class PraetorsGraspEffect extends OneShotEffect { UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (card != null && exileId != null) { game.informPlayers(controller.getLogName() + " moves the searched card face down to exile"); - card.moveToExile(exileId, sourceObject.getName(), source.getSourceId(), game); + card.moveToExile(exileId, sourceObject.getIdName(), source.getSourceId(), game); card.setFaceDown(true, game); game.addEffect(new PraetorsGraspPlayEffect(card.getId()), source); game.addEffect(new PraetorsGraspRevealEffect(card.getId()), source); @@ -144,12 +142,11 @@ class PraetorsGraspPlayEffect extends AsThoughEffectImpl { } @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (sourceId.equals(cardId)) { + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (objectId.equals(cardId) && affectedControllerId.equals(source.getControllerId())) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject, true); - if (exileId != null && sourceObject != null && controller != null) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + if (exileId != null && controller != null) { ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone != null && exileZone.contains(cardId)) { if (controller.chooseUse(outcome, "Play the exiled card?", game)) { @@ -191,10 +188,10 @@ class PraetorsGraspRevealEffect extends AsThoughEffectImpl { } @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (sourceId.equals(cardId)) { + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (objectId.equals(cardId) && affectedControllerId.equals(source.getControllerId())) { MageObject sourceObject = source.getSourceObject(game); - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject, true); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (exileId != null && sourceObject != null) { ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone != null && exileZone.contains(cardId)) { @@ -203,7 +200,7 @@ class PraetorsGraspRevealEffect extends AsThoughEffectImpl { if (controller != null && card != null && game.getState().getZone(cardId) == Zone.EXILED) { if (controller.chooseUse(outcome, "Reveal exiled card?", game)) { Cards cards = new CardsImpl(card); - controller.lookAtCards("Exiled with " + sourceObject.getName(), cards, game); + controller.lookAtCards("Exiled with " + sourceObject.getIdName(), cards, game); } } } else { diff --git a/Mage.Sets/src/mage/sets/newphyrexia/SensorSplicer.java b/Mage.Sets/src/mage/sets/newphyrexia/SensorSplicer.java index e0bdbc916ca..6a360c51e5b 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/SensorSplicer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/SensorSplicer.java @@ -65,7 +65,10 @@ public class SensorSplicer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken()))); + // When Sensor Splicer enters the battlefield, put a 3/3 colorless Golem artifact creature token onto the battlefield. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(expansionSetCode)))); + + // Golem creatures you control have vigilance. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, filter))); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/SheoldredWhisperingOne.java b/Mage.Sets/src/mage/sets/newphyrexia/SheoldredWhisperingOne.java index cc7e64b8930..fa84cf9d8d2 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/SheoldredWhisperingOne.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/SheoldredWhisperingOne.java @@ -58,10 +58,16 @@ public class SheoldredWhisperingOne extends CardImpl { this.power = new MageInt(6); this.toughness = new MageInt(6); + + // Swampwalk this.addAbility(new SwampwalkAbility()); + + // At the beginning of your upkeep, return target creature card from your graveyard to the battlefield. Ability ability = new BeginningOfUpkeepTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false), TargetController.YOU, false); ability.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card from your graveyard"))); this.addAbility(ability); + + // At the beginning of each opponent's upkeep, that player sacrifices a creature. ability = new BeginningOfUpkeepTriggeredAbility(new SacrificeEffect(new FilterCreaturePermanent(), 1, "that player "), TargetController.OPPONENT, false); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/TorporOrb.java b/Mage.Sets/src/mage/sets/newphyrexia/TorporOrb.java index 22e0e3103fa..59519623423 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/TorporOrb.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/TorporOrb.java @@ -28,8 +28,8 @@ package mage.sets.newphyrexia; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.cards.CardImpl; @@ -94,12 +94,16 @@ class TorporOrbEffect extends ContinuousRuleModifyingEffectImpl { } return false; } - @Override - public boolean apply(Game game, Ability source) { - return true; + public String getInfoMessage(Ability source, GameEvent event, Game game) { + MageObject mageObject = game.getObject(event.getSourceId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (mageObject != null && sourceObject != null) { + return sourceObject.getLogName() + " prevented ability of " + mageObject.getLogName() + " to trigger"; + } + return null; } - + @Override public TorporOrbEffect copy() { return new TorporOrbEffect(this); diff --git a/Mage.Sets/src/mage/sets/newphyrexia/VitalSplicer.java b/Mage.Sets/src/mage/sets/newphyrexia/VitalSplicer.java index 251e9e2f4a2..48400adbe7e 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/VitalSplicer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/VitalSplicer.java @@ -71,7 +71,7 @@ public class VitalSplicer extends CardImpl { this.toughness = new MageInt(1); // When Vital Splicer enters the battlefield, put a 3/3 colorless Golem artifact creature token onto the battlefield. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken()))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(expansionSetCode)))); // {1}: Regenerate target Golem you control. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateTargetEffect(), new ManaCostsImpl("{1}")); diff --git a/Mage.Sets/src/mage/sets/newphyrexia/WingSplicer.java b/Mage.Sets/src/mage/sets/newphyrexia/WingSplicer.java index 417ad19da78..1716220359a 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/WingSplicer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/WingSplicer.java @@ -66,7 +66,10 @@ public class WingSplicer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken()))); + // When Wing Splicer enters the battlefield, put a 3/3 colorless Golem artifact creature token onto the battlefield. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(expansionSetCode)))); + + // Golem creatures you control have flying. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, filter))); } diff --git a/Mage.Sets/src/mage/sets/ninthedition/Deathgazer.java b/Mage.Sets/src/mage/sets/ninthedition/Deathgazer.java index fa3a2db0fee..6bd0b64e1dd 100644 --- a/Mage.Sets/src/mage/sets/ninthedition/Deathgazer.java +++ b/Mage.Sets/src/mage/sets/ninthedition/Deathgazer.java @@ -87,7 +87,7 @@ class DeathgazerTriggeredAbility extends TriggeredAbilityImpl { if (event.getType() == GameEvent.EventType.BLOCKER_DECLARED) { if (event.getSourceId().equals(this.getSourceId())) { Permanent permanent = game.getPermanent(event.getTargetId()); - if(permanent != null && !permanent.getColor().isBlack()) + if(permanent != null && !permanent.getColor(game).isBlack()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); @@ -97,7 +97,7 @@ class DeathgazerTriggeredAbility extends TriggeredAbilityImpl { } if (event.getTargetId().equals(this.getSourceId())) { Permanent permanent = game.getPermanent(event.getSourceId()); - if(permanent != null && !permanent.getColor().isBlack()) + if(permanent != null && !permanent.getColor(game).isBlack()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getSourceId())); diff --git a/Mage.Sets/src/mage/sets/ninthedition/GoblinBrigand.java b/Mage.Sets/src/mage/sets/ninthedition/GoblinBrigand.java new file mode 100644 index 00000000000..c25490133c9 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ninthedition/GoblinBrigand.java @@ -0,0 +1,63 @@ +/* + * 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.sets.ninthedition; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.AttacksEachTurnStaticAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class GoblinBrigand extends CardImpl { + + public GoblinBrigand(UUID ownerId) { + super(ownerId, 190, "Goblin Brigand", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); + this.expansionSetCode = "9ED"; + this.subtype.add("Goblin"); + this.subtype.add("Warrior"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Goblin Brigand attacks each turn if able. + this.addAbility(new AttacksEachTurnStaticAbility()); + } + + public GoblinBrigand(final GoblinBrigand card) { + super(card); + } + + @Override + public GoblinBrigand copy() { + return new GoblinBrigand(this); + } +} diff --git a/Mage.Sets/src/mage/sets/ninthedition/GravePact.java b/Mage.Sets/src/mage/sets/ninthedition/GravePact.java index 502ff727012..08c949d9b2e 100644 --- a/Mage.Sets/src/mage/sets/ninthedition/GravePact.java +++ b/Mage.Sets/src/mage/sets/ninthedition/GravePact.java @@ -27,6 +27,8 @@ */ package mage.sets.ninthedition; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import mage.constants.CardType; import mage.constants.Outcome; @@ -41,7 +43,6 @@ import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -121,18 +122,28 @@ class GravePactEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - for (UUID playerId : game.getPlayerList()) { - if (!playerId.equals(source.getControllerId())) { + List perms = new ArrayList<>(); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId : controller.getInRange()) { Player player = game.getPlayer(playerId); - Target target = new TargetControlledCreaturePermanent(); - if (player != null && player.choose(Outcome.Sacrifice, target, source.getSourceId(), game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); + if (player != null && !playerId.equals(source.getControllerId())) { + TargetControlledCreaturePermanent target = new TargetControlledCreaturePermanent(); + target.setNotTarget(true); + if (target.canChoose(player.getId(), game)) { + player.chooseTarget(Outcome.Sacrifice, target, source, game); + perms.addAll(target.getTargets()); } } } + for (UUID permID : perms) { + Permanent permanent = game.getPermanent(permID); + if (permanent != null) { + permanent.sacrifice(source.getSourceId(), game); + } + } + return true; } - return false; + return false; } } diff --git a/Mage.Sets/src/mage/sets/odyssey/EarnestFellowship.java b/Mage.Sets/src/mage/sets/odyssey/EarnestFellowship.java index 4014969ef3f..583ed4bcfe5 100644 --- a/Mage.Sets/src/mage/sets/odyssey/EarnestFellowship.java +++ b/Mage.Sets/src/mage/sets/odyssey/EarnestFellowship.java @@ -97,9 +97,9 @@ class EarnestFellowshipEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { - if (permanent.getColor().hasColor()) { + if (permanent.getColor(game).hasColor()) { List colorPredicates = new ArrayList<>(); - for (ObjectColor color : permanent.getColor().getColors()) { + for (ObjectColor color : permanent.getColor(game).getColors()) { colorPredicates.add(new ColorPredicate(color)); } FilterCard filterColors = new FilterCard("its colors"); diff --git a/Mage.Sets/src/mage/sets/odyssey/RayOfDistortion.java b/Mage.Sets/src/mage/sets/odyssey/RayOfDistortion.java index 36233bea58c..a57877773cf 100644 --- a/Mage.Sets/src/mage/sets/odyssey/RayOfDistortion.java +++ b/Mage.Sets/src/mage/sets/odyssey/RayOfDistortion.java @@ -35,9 +35,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.TimingRule; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -46,20 +44,13 @@ import mage.target.TargetPermanent; */ public class RayOfDistortion extends CardImpl { - static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ENCHANTMENT))); - } - public RayOfDistortion(UUID ownerId) { super(ownerId, 42, "Ray of Distortion", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{3}{W}"); this.expansionSetCode = "ODY"; - // Destroy target artifact or enchantment. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); // Flashback {4}{W}{W} this.addAbility(new FlashbackAbility(new ManaCostsImpl("{4}{W}{W}"), TimingRule.INSTANT)); } diff --git a/Mage.Sets/src/mage/sets/odyssey/TestamentOfFaith.java b/Mage.Sets/src/mage/sets/odyssey/TestamentOfFaith.java index 4b9149507ce..c19111128ea 100644 --- a/Mage.Sets/src/mage/sets/odyssey/TestamentOfFaith.java +++ b/Mage.Sets/src/mage/sets/odyssey/TestamentOfFaith.java @@ -127,8 +127,8 @@ class TestamentOfFaithBecomesCreatureSourceEffect extends ContinuousEffectImpl i break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - if (token.getColor().hasColor()) { - permanent.getColor().setColor(token.getColor()); + if (token.getColor(game).hasColor()) { + permanent.getColor(game).setColor(token.getColor(game)); } } break; diff --git a/Mage.Sets/src/mage/sets/onslaught/MythicProportions.java b/Mage.Sets/src/mage/sets/onslaught/MythicProportions.java new file mode 100644 index 00000000000..d5e6dc8b23b --- /dev/null +++ b/Mage.Sets/src/mage/sets/onslaught/MythicProportions.java @@ -0,0 +1,82 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.onslaught; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Jgod + */ +public class MythicProportions extends CardImpl { + + public MythicProportions(UUID ownerId) { + super(ownerId, 274, "Mythic Proportions", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{4}{G}{G}{G}"); + this.expansionSetCode = "ONS"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +8/+8 and has trample. + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(8, 8)); + Effect effect = new GainAbilityAttachedEffect(TrampleAbility.getInstance(), AttachmentType.AURA); + effect.setText("and has trample"); + ability.addEffect(effect); + this.addAbility(ability); + } + + public MythicProportions(final MythicProportions card) { + super(card); + } + + @Override + public MythicProportions copy() { + return new MythicProportions(this); + } +} diff --git a/Mage.Sets/src/mage/sets/onslaught/SearingFlesh.java b/Mage.Sets/src/mage/sets/onslaught/SearingFlesh.java new file mode 100644 index 00000000000..82fb9f8353d --- /dev/null +++ b/Mage.Sets/src/mage/sets/onslaught/SearingFlesh.java @@ -0,0 +1,60 @@ +/* + * 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.sets.onslaught; + +import java.util.UUID; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.target.common.TargetOpponent; + +/** + * + * @author Jgod + */ +public class SearingFlesh extends CardImpl { + + public SearingFlesh(UUID ownerId) { + super(ownerId, 225, "Searing Flesh", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{6}{R}"); + this.expansionSetCode = "ONS"; + + // Searing Flesh deals 7 damage to target opponent. + this.getSpellAbility().addTarget(new TargetOpponent()); + this.getSpellAbility().addEffect(new DamageTargetEffect(7)); + } + + public SearingFlesh(final SearingFlesh card) { + super(card); + } + + @Override + public SearingFlesh copy() { + return new SearingFlesh(this); + } +} diff --git a/Mage.Sets/src/mage/sets/planarchaos/BoomBust.java b/Mage.Sets/src/mage/sets/planarchaos/BoomBust.java index 5f9bd3bf3b3..b73609a114a 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/BoomBust.java +++ b/Mage.Sets/src/mage/sets/planarchaos/BoomBust.java @@ -60,11 +60,8 @@ public class BoomBust extends SplitCard { super(ownerId, 112, "Boom", "Bust", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{R}", "{5}{R}", false); this.expansionSetCode = "PLC"; - this.color.setRed(true); - // Boom // Destroy target land you control and target land you don't control. - getLeftHalfCard().getColor().setRed(true); Effect effect = new DestroyTargetEffect(); effect.setText("Destroy target land you control and target land you don't control"); getLeftHalfCard().getSpellAbility().addEffect(effect); @@ -73,7 +70,6 @@ public class BoomBust extends SplitCard { // Bust // Destroy all lands. - getRightHalfCard().getColor().setRed(true); getRightHalfCard().getSpellAbility().addEffect(new DestroyAllEffect(new FilterLandPermanent())); } diff --git a/Mage.Sets/src/mage/sets/planarchaos/DeadGone.java b/Mage.Sets/src/mage/sets/planarchaos/DeadGone.java index ceab6f00f7c..199da64c059 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/DeadGone.java +++ b/Mage.Sets/src/mage/sets/planarchaos/DeadGone.java @@ -1,6 +1,5 @@ package mage.sets.planarchaos; -import mage.abilities.Mode; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.Card; @@ -27,17 +26,14 @@ public class DeadGone extends SplitCard { public DeadGone(UUID ownerId) { super(ownerId, 113, "Dead", "Gone", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}", "{2}{R}", false); this.expansionSetCode = "PLC"; - this.color.setRed(true); // Dead // Dead deals 2 damage to target creature. - getLeftHalfCard().getColor().setRed(true); getLeftHalfCard().getSpellAbility().addEffect(new DeadDamageEffect()); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); // Gone // Return target creature you don't control to its owner's hand. - getRightHalfCard().getColor().setRed(true); getRightHalfCard().getSpellAbility().addEffect(new ReturnToHandTargetEffect()); getRightHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); } diff --git a/Mage.Sets/src/mage/sets/planarchaos/DustElemental.java b/Mage.Sets/src/mage/sets/planarchaos/DustElemental.java new file mode 100644 index 00000000000..2e910edd37a --- /dev/null +++ b/Mage.Sets/src/mage/sets/planarchaos/DustElemental.java @@ -0,0 +1,75 @@ +/* + * 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.sets.planarchaos; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnToHandChosenControlledPermanentEffect; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.FearAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.common.FilterControlledCreaturePermanent; + +/** + * + * @author Zeplar1_at_googlemail.com + */ +public class DustElemental extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(" creatures you control"); + + public DustElemental(UUID ownerId) { + super(ownerId, 5, "Dust Elemental", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); + this.expansionSetCode = "PLC"; + this.subtype.add("Elemental"); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Flash + this.addAbility(FlashAbility.getInstance()); + // Flying; fear + this.addAbility(FlyingAbility.getInstance()); + this.addAbility(FearAbility.getInstance()); + + // When Dust Elemental enters the battlefield, return three creatures you control to their owner's hand. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(filter, 3))); + } + + public DustElemental(final DustElemental card) { + super(card); + } + + @Override + public DustElemental copy() { + return new DustElemental(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/planarchaos/LifeAndLimb.java b/Mage.Sets/src/mage/sets/planarchaos/LifeAndLimb.java index ef75978269e..09813d3f59c 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/LifeAndLimb.java +++ b/Mage.Sets/src/mage/sets/planarchaos/LifeAndLimb.java @@ -116,7 +116,7 @@ class LifeAndLimbEffect extends ContinuousEffectImpl { } break; case ColorChangingEffects_5: - permanent.getColor().setColor(ObjectColor.GREEN); + permanent.getColor(game).setColor(ObjectColor.GREEN); break; case AbilityAddingRemovingEffects_6: boolean flag = false; diff --git a/Mage.Sets/src/mage/sets/planarchaos/SealOfPrimordium.java b/Mage.Sets/src/mage/sets/planarchaos/SealOfPrimordium.java index 91d38aa731d..4dcee2ae1b4 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/SealOfPrimordium.java +++ b/Mage.Sets/src/mage/sets/planarchaos/SealOfPrimordium.java @@ -28,7 +28,6 @@ package mage.sets.planarchaos; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.Ability; @@ -37,9 +36,7 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -48,22 +45,13 @@ import mage.target.TargetPermanent; */ public class SealOfPrimordium extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public SealOfPrimordium(UUID ownerId) { super(ownerId, 153, "Seal of Primordium", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); this.expansionSetCode = "PLC"; - // Sacrifice Seal of Primordium: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/planarchaos/StormfrontRiders.java b/Mage.Sets/src/mage/sets/planarchaos/StormfrontRiders.java index 13f78771209..e9804d375cb 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/StormfrontRiders.java +++ b/Mage.Sets/src/mage/sets/planarchaos/StormfrontRiders.java @@ -39,7 +39,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.other.OwnerPredicate; import mage.game.permanent.token.SoldierToken; @@ -68,7 +68,7 @@ public class StormfrontRiders extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Stormfront Riders enters the battlefield, return two creatures you control to their owner's hand. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(new FilterControlledPermanent("creatures you control"), 2))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(new FilterControlledCreaturePermanent("creatures you control"), 2))); // Whenever Stormfront Riders or another creature is returned to your hand from the battlefield, put a 1/1 white Soldier creature token onto the battlefield. this.addAbility(new ZoneChangeAllTriggeredAbility(Zone.BATTLEFIELD, Zone.BATTLEFIELD, Zone.HAND, new CreateTokenEffect(new SoldierToken()), filter,"Whenever {this} or another creature is returned to your hand from the battlefield, ", false)); diff --git a/Mage.Sets/src/mage/sets/planechase2012/NullmageAdvocate.java b/Mage.Sets/src/mage/sets/planechase2012/NullmageAdvocate.java index 6d89ed28723..358c04d9e5f 100644 --- a/Mage.Sets/src/mage/sets/planechase2012/NullmageAdvocate.java +++ b/Mage.Sets/src/mage/sets/planechase2012/NullmageAdvocate.java @@ -40,9 +40,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.filter.FilterCard; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; import mage.target.common.TargetCardInOpponentsGraveyard; import mage.target.targetpointer.SecondTargetPointer; @@ -53,17 +51,11 @@ import mage.target.targetpointer.SecondTargetPointer; */ public class NullmageAdvocate extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent(); - static { - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ENCHANTMENT))); - } - public NullmageAdvocate(UUID ownerId) { super(ownerId, 70, "Nullmage Advocate", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{G}"); this.expansionSetCode = "PC2"; this.subtype.add("Insect"); this.subtype.add("Druid"); - this.power = new MageInt(2); this.toughness = new MageInt(3); @@ -76,7 +68,7 @@ public class NullmageAdvocate extends CardImpl { effect.setTargetPointer(new SecondTargetPointer()); ability.addEffect(effect); ability.addTarget(new TargetCardInOpponentsGraveyard(2,2, new FilterCard("two target cards from an opponent's graveyard"), true)); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java b/Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java index 6c811609580..bcf4d89300b 100644 --- a/Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java +++ b/Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java @@ -34,7 +34,7 @@ import mage.abilities.costs.common.ExileFromHandCost; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; import mage.abilities.effects.common.UntapSourceEffect; -import mage.abilities.mana.ColorlessManaAbility; +import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; @@ -45,7 +45,7 @@ import mage.target.common.TargetCardInHand; /** * - * @author anonymous + * @author Luna Skyrise */ public class ForsakenCity extends CardImpl { @@ -57,10 +57,12 @@ public class ForsakenCity extends CardImpl { // Forsaken City doesn't untap during your untap step. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepSourceEffect())); + // At the beginning of your upkeep, you may exile a card from your hand. If you do, untap Forsaken City. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DoIfCostPaid(new UntapSourceEffect(), new ExileFromHandCost(new TargetCardInHand(filter))), TargetController.YOU, true)); - // {tap}: Add one mana of any color to your mana pool. - this.addAbility(new ColorlessManaAbility()); + + // {T}: Add one mana of any color to your mana pool. + this.addAbility(new AnyColorManaAbility()); } public ForsakenCity(final ForsakenCity card) { diff --git a/Mage.Sets/src/mage/sets/planeshift/MeteorCrater.java b/Mage.Sets/src/mage/sets/planeshift/MeteorCrater.java index 813ac559688..c64976c569b 100644 --- a/Mage.Sets/src/mage/sets/planeshift/MeteorCrater.java +++ b/Mage.Sets/src/mage/sets/planeshift/MeteorCrater.java @@ -178,7 +178,7 @@ class MeteorCraterEffect extends ManaEffect { List controlledPermanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game); Mana types = new Mana(); for (Permanent permanent : controlledPermanents) { - ObjectColor color = permanent.getColor(); + ObjectColor color = permanent.getColor(game); if (color.isBlack()) { types.add(Mana.BlackMana); } diff --git a/Mage.Sets/src/mage/sets/planeshift/ShiftingSky.java b/Mage.Sets/src/mage/sets/planeshift/ShiftingSky.java index c856eb587af..cf386bea42c 100644 --- a/Mage.Sets/src/mage/sets/planeshift/ShiftingSky.java +++ b/Mage.Sets/src/mage/sets/planeshift/ShiftingSky.java @@ -28,7 +28,6 @@ package mage.sets.planeshift; import java.util.UUID; -import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -113,7 +112,7 @@ class ShiftingSkyEffect extends OneShotEffect { Player p = game.getPlayer(playerId); if (p != null) { for (Permanent chosen : game.getBattlefield().getAllActivePermanents(filter, playerId, game)) { - setObject(chosen, colorString); + setObject(chosen, colorString, game); } } } @@ -127,22 +126,22 @@ class ShiftingSkyEffect extends OneShotEffect { return new ShiftingSkyEffect(this); } - private void setObject(Permanent chosen, String colorString) { + private void setObject(Permanent chosen, String colorString, Game game) { switch (colorString) { case "W": - chosen.getColor().setWhite(true); + chosen.getColor(game).setWhite(true); break; case "B": - chosen.getColor().setBlack(true); + chosen.getColor(game).setBlack(true); break; case "U": - chosen.getColor().setBlue(true); + chosen.getColor(game).setBlue(true); break; case "G": - chosen.getColor().setGreen(true); + chosen.getColor(game).setGreen(true); break; case "R": - chosen.getColor().setRed(true); + chosen.getColor(game).setRed(true); break; } } diff --git a/Mage.Sets/src/mage/sets/portalsecondage/FalseSummoning.java b/Mage.Sets/src/mage/sets/portalsecondage/FalseSummoning.java new file mode 100644 index 00000000000..8e52b122a37 --- /dev/null +++ b/Mage.Sets/src/mage/sets/portalsecondage/FalseSummoning.java @@ -0,0 +1,61 @@ +/* + * 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.sets.portalsecondage; + +import java.util.UUID; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.filter.common.FilterCreatureSpell; +import mage.target.TargetSpell; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class FalseSummoning extends CardImpl { + + public FalseSummoning(UUID ownerId) { + super(ownerId, 40, "False Summoning", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{U}"); + this.expansionSetCode = "PO2"; + + // Counter target creature spell. + this.getSpellAbility().addTarget(new TargetSpell(new FilterCreatureSpell())); + this.getSpellAbility().addEffect(new CounterTargetEffect()); + } + + public FalseSummoning(final FalseSummoning card) { + super(card); + } + + @Override + public FalseSummoning copy() { + return new FalseSummoning(this); + } +} diff --git a/Mage.Sets/src/mage/sets/portalthreekingdoms/PreemptiveStrike.java b/Mage.Sets/src/mage/sets/portalthreekingdoms/PreemptiveStrike.java new file mode 100644 index 00000000000..3d5aa3ce4f0 --- /dev/null +++ b/Mage.Sets/src/mage/sets/portalthreekingdoms/PreemptiveStrike.java @@ -0,0 +1,61 @@ +/* + * 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.sets.portalthreekingdoms; + +import java.util.UUID; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.filter.common.FilterCreatureSpell; +import mage.target.TargetSpell; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class PreemptiveStrike extends CardImpl { + + public PreemptiveStrike(UUID ownerId) { + super(ownerId, 50, "Preemptive Strike", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{U}"); + this.expansionSetCode = "PTK"; + + // Counter target creature spell. + this.getSpellAbility().addTarget(new TargetSpell(new FilterCreatureSpell())); + this.getSpellAbility().addEffect(new CounterTargetEffect()); + } + + public PreemptiveStrike(final PreemptiveStrike card) { + super(card); + } + + @Override + public PreemptiveStrike copy() { + return new PreemptiveStrike(this); + } +} diff --git a/Mage.Sets/src/mage/sets/ravnica/CrownOfConvergence.java b/Mage.Sets/src/mage/sets/ravnica/CrownOfConvergence.java index d65d95c083c..4493234abf8 100644 --- a/Mage.Sets/src/mage/sets/ravnica/CrownOfConvergence.java +++ b/Mage.Sets/src/mage/sets/ravnica/CrownOfConvergence.java @@ -104,7 +104,7 @@ class CrownOfConvergenceColorBoostEffect extends BoostAllEffect { Card topCard = you.getLibrary().getFromTop(game); if (topCard != null) { for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (permanent.getColor().shares(topCard.getColor()) && !permanent.getColor().isColorless()) { + if (permanent.getColor(game).shares(topCard.getColor(game)) && !permanent.getColor(game).isColorless()) { permanent.addPower(power.calculate(game, source, this)); permanent.addToughness(toughness.calculate(game, source, this)); } diff --git a/Mage.Sets/src/mage/sets/ravnica/DimirDoppelganger.java b/Mage.Sets/src/mage/sets/ravnica/DimirDoppelganger.java index 9cc2cf0d14b..d1354b7a3a5 100644 --- a/Mage.Sets/src/mage/sets/ravnica/DimirDoppelganger.java +++ b/Mage.Sets/src/mage/sets/ravnica/DimirDoppelganger.java @@ -107,7 +107,7 @@ class DimirDoppelgangerEffect extends ContinuousEffectImpl { permanent.setName(cardToCopy.getName()); permanent.getPower().setValue(cardToCopy.getPower().getValue()); permanent.getToughness().setValue(cardToCopy.getToughness().getValue()); - permanent.getColor().setColor(cardToCopy.getColor()); + permanent.getColor(game).setColor(cardToCopy.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(cardToCopy.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage.Sets/src/mage/sets/ravnica/LeaveNoTrace.java b/Mage.Sets/src/mage/sets/ravnica/LeaveNoTrace.java index cf9a98f6318..ac98d00f4a2 100644 --- a/Mage.Sets/src/mage/sets/ravnica/LeaveNoTrace.java +++ b/Mage.Sets/src/mage/sets/ravnica/LeaveNoTrace.java @@ -92,10 +92,10 @@ class LeaveNoTraceEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent target = game.getPermanent(targetPointer.getFirst(game, source)); if (target != null) { - ObjectColor color = target.getColor(); + ObjectColor color = target.getColor(game); target.destroy(source.getSourceId(), game, false); for (Permanent p : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { - if (p.getColor().shares(color)) { + if (p.getColor(game).shares(color)) { p.destroy(source.getSourceId(), game, false); } } diff --git a/Mage.Sets/src/mage/sets/ravnica/NullmageShepherd.java b/Mage.Sets/src/mage/sets/ravnica/NullmageShepherd.java index ede068e74eb..160b53f7c57 100644 --- a/Mage.Sets/src/mage/sets/ravnica/NullmageShepherd.java +++ b/Mage.Sets/src/mage/sets/ravnica/NullmageShepherd.java @@ -28,7 +28,6 @@ package mage.sets.ravnica; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; @@ -38,10 +37,9 @@ import mage.abilities.costs.common.TapTargetCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.Zone; -import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.filter.predicate.permanent.TappedPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -53,13 +51,8 @@ import mage.target.common.TargetControlledCreaturePermanent; public class NullmageShepherd extends CardImpl { private static final FilterControlledCreaturePermanent filterCost = new FilterControlledCreaturePermanent("untapped creatures you control"); - private static final FilterPermanent filterTarget = new FilterPermanent("artifact or enchantment"); - static { filterCost.add(Predicates.not(new TappedPredicate())); - filterTarget.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); } public NullmageShepherd(UUID ownerId) { @@ -73,7 +66,7 @@ public class NullmageShepherd extends CardImpl { // Tap four untapped creatures you control: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new TapTargetCost(new TargetControlledCreaturePermanent(4, 4, filterCost, true))); - ability.addTarget(new TargetPermanent(filterTarget)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/ravnica/RallyTheRighteous.java b/Mage.Sets/src/mage/sets/ravnica/RallyTheRighteous.java index 077dfb18391..71ee000110e 100644 --- a/Mage.Sets/src/mage/sets/ravnica/RallyTheRighteous.java +++ b/Mage.Sets/src/mage/sets/ravnica/RallyTheRighteous.java @@ -93,10 +93,10 @@ class RallyTheRighteousUntapEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent target = game.getPermanent(getTargetPointer().getFirst(game, source)); if (target != null) { - ObjectColor color = target.getColor(); + ObjectColor color = target.getColor(game); target.untap(game); for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { - if (permanent.getColor().shares(color) && !permanent.getId().equals(target.getId())) { + if (permanent.getColor(game).shares(color) && !permanent.getId().equals(target.getId())) { permanent.untap(game); } } @@ -124,10 +124,10 @@ class RallyTheRighteousBoostEffect extends ContinuousEffectImpl { Permanent target = game.getPermanent(getTargetPointer().getFirst(game, source)); if (target != null) { affectedObjectList.add(new MageObjectReference(target, game)); - ObjectColor color = target.getColor(); + ObjectColor color = target.getColor(game); target.addPower(2); for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { - if (!permanent.getId().equals(target.getId()) && permanent.getColor().shares(color)) { + if (!permanent.getId().equals(target.getId()) && permanent.getColor(game).shares(color)) { affectedObjectList.add(new MageObjectReference(permanent, game)); } } diff --git a/Mage.Sets/src/mage/sets/ravnica/SunderingVitae.java b/Mage.Sets/src/mage/sets/ravnica/SunderingVitae.java index bf5465aa574..208e25aa391 100644 --- a/Mage.Sets/src/mage/sets/ravnica/SunderingVitae.java +++ b/Mage.Sets/src/mage/sets/ravnica/SunderingVitae.java @@ -33,9 +33,7 @@ import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.ConvokeAbility; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -43,24 +41,15 @@ import mage.target.TargetPermanent; * @author jonubuu */ public class SunderingVitae extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public SunderingVitae(UUID ownerId) { super(ownerId, 185, "Sundering Vitae", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{G}"); this.expansionSetCode = "RAV"; - // Convoke this.addAbility(new ConvokeAbility()); // Destroy target artifact or enchantment. - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/sets/ravnica/ThundersongTrumpeter.java b/Mage.Sets/src/mage/sets/ravnica/ThundersongTrumpeter.java new file mode 100644 index 00000000000..c487030aac2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ravnica/ThundersongTrumpeter.java @@ -0,0 +1,71 @@ +/* + * 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.sets.ravnica; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.combat.CantBlockTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Jgod + */ +public class ThundersongTrumpeter extends CardImpl { + + public ThundersongTrumpeter(UUID ownerId) { + super(ownerId, 235, "Thundersong Trumpeter", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{R}{W}"); + this.expansionSetCode = "RAV"; + this.subtype.add("Human"); + this.subtype.add("Soldier"); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // {tap}: Target creature can't attack or block this turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBlockTargetEffect(Duration.EndOfTurn), new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public ThundersongTrumpeter(final ThundersongTrumpeter card) { + super(card); + } + + @Override + public ThundersongTrumpeter copy() { + return new ThundersongTrumpeter(this); + } +} diff --git a/Mage.Sets/src/mage/sets/ravnica/WateryGrave.java b/Mage.Sets/src/mage/sets/ravnica/WateryGrave.java index 526a60a6535..df912b539d6 100644 --- a/Mage.Sets/src/mage/sets/ravnica/WateryGrave.java +++ b/Mage.Sets/src/mage/sets/ravnica/WateryGrave.java @@ -49,6 +49,7 @@ public class WateryGrave extends CardImpl { this.expansionSetCode = "RAV"; this.subtype.add("Island"); this.subtype.add("Swamp"); + this.addAbility(new BlueManaAbility()); this.addAbility(new BlackManaAbility()); this.addAbility(new AsEntersBattlefieldAbility(new TapSourceUnlessPaysEffect(new PayLifeCost(2)), "you may pay 2 life. If you don't, {this} enters the battlefield tapped")); diff --git a/Mage.Sets/src/mage/sets/returntoravnica/CivicSaber.java b/Mage.Sets/src/mage/sets/returntoravnica/CivicSaber.java index d463ae79988..9274fc8cd53 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/CivicSaber.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/CivicSaber.java @@ -84,7 +84,7 @@ class CivicSaberColorCount implements DynamicValue { if (equipment != null) { Permanent permanent = game.getPermanent(equipment.getAttachedTo()); if (permanent != null) { - count = permanent.getColor().getColorCount(); + count = permanent.getColor(game).getColorCount(); } } return count; diff --git a/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java b/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java index 9ca8f30b745..c83780397b9 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java @@ -195,7 +195,7 @@ class GraveBetrayalContiniousEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - creature.getColor().setBlack(true); + creature.getColor(game).setBlack(true); } break; } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/GrislySalvage.java b/Mage.Sets/src/mage/sets/returntoravnica/GrislySalvage.java index 0b1ee4f9c85..e030085eb52 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/GrislySalvage.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/GrislySalvage.java @@ -56,7 +56,6 @@ public class GrislySalvage extends CardImpl { super(ownerId, 165, "Grisly Salvage", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{B}{G}"); this.expansionSetCode = "RTR"; - // Reveal the top five cards of your library. You may put a creature or land card from among them into your hand. Put the rest into your graveyard. this.getSpellAbility().addEffect(new GrislySalvageEffect()); } @@ -109,7 +108,8 @@ class GrislySalvageEffect extends OneShotEffect { if (!cards.isEmpty()) { controller.revealCards(sourceObject.getName(), cards, game); TargetCard target = new TargetCard(Zone.LIBRARY, filterPutInHand); - if (properCardFound && controller.choose(Outcome.DrawCard, cards, target, game)) { + if (properCardFound && controller.chooseUse(outcome, "Put a creature or land card from the revealed cards into your hand?", game) && + controller.choose(Outcome.DrawCard, cards, target, game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { controller.moveCards(card, Zone.LIBRARY, Zone.HAND, source, game); diff --git a/Mage.Sets/src/mage/sets/returntoravnica/LobberCrew.java b/Mage.Sets/src/mage/sets/returntoravnica/LobberCrew.java index 9d8a1a6b558..74623e0cfbe 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/LobberCrew.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/LobberCrew.java @@ -97,7 +97,7 @@ class LobberCrewTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isMulticolored() && event.getPlayerId().equals(getControllerId())) { + if (spell != null && spell.getColor(game).isMulticolored() && event.getPlayerId().equals(getControllerId())) { return true; } } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/Pyroconvergence.java b/Mage.Sets/src/mage/sets/returntoravnica/Pyroconvergence.java index cb2b6d91a03..1bbf63022c7 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/Pyroconvergence.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/Pyroconvergence.java @@ -85,7 +85,7 @@ class PyroconvergenceTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isMulticolored() && event.getPlayerId().equals(getControllerId())) { + if (spell != null && spell.getColor(game).isMulticolored() && event.getPlayerId().equals(getControllerId())) { return true; } } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/RakdosCharm.java b/Mage.Sets/src/mage/sets/returntoravnica/RakdosCharm.java index d6b4a5f73fe..5ad478abf5c 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/RakdosCharm.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/RakdosCharm.java @@ -55,7 +55,6 @@ public class RakdosCharm extends CardImpl { super(ownerId, 184, "Rakdos Charm", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{B}{R}"); this.expansionSetCode = "RTR"; - // Choose one — Exile all cards from target player's graveyard; this.getSpellAbility().addEffect(new ExileGraveyardAllTargetPlayerEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); @@ -81,7 +80,6 @@ public class RakdosCharm extends CardImpl { return new RakdosCharm(this); } - private class RakdosCharmDamageEffect extends OneShotEffect { public RakdosCharmDamageEffect() { @@ -113,6 +111,5 @@ public class RakdosCharm extends CardImpl { public RakdosCharmDamageEffect copy() { return new RakdosCharmDamageEffect(this); } - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/returntoravnica/RestInPeace.java b/Mage.Sets/src/mage/sets/returntoravnica/RestInPeace.java index 0462939b00b..e2adf646e2d 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/RestInPeace.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/RestInPeace.java @@ -31,8 +31,8 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -72,9 +72,8 @@ public class RestInPeace extends CardImpl { super(ownerId, 18, "Rest in Peace", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); this.expansionSetCode = "RTR"; - // When Rest in Peace enters the battlefield, exile all cards from all graveyards. - this.addAbility(new EntersBattlefieldTriggeredAbility(new RestInPeaceExileAllEffect())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ExileGraveyardAllPlayersEffect())); // If a card or token would be put into a graveyard from anywhere, exile it instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new RestInPeaceReplacementEffect())); @@ -90,43 +89,6 @@ public class RestInPeace extends CardImpl { } } -class RestInPeaceExileAllEffect extends OneShotEffect { - - public RestInPeaceExileAllEffect() { - super(Outcome.Detriment); - staticText = "exile all cards from all graveyards"; - } - - public RestInPeaceExileAllEffect(final RestInPeaceExileAllEffect effect) { - super(effect); - } - - @Override - public RestInPeaceExileAllEffect copy() { - return new RestInPeaceExileAllEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID playerId : controller.getInRange()) { - Player player = game.getPlayer(playerId); - if (player != null) { - for (UUID cid : player.getGraveyard().copy()) { - Card card = game.getCard(cid); - if (card != null) { - controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.GRAVEYARD, true); - } - } - } - } - return true; - } - return false; - } -} - class RestInPeaceReplacementEffect extends ReplacementEffectImpl { public RestInPeaceReplacementEffect() { @@ -173,5 +135,4 @@ class RestInPeaceReplacementEffect extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { return ((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD; } - } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/TabletOfTheGuilds.java b/Mage.Sets/src/mage/sets/returntoravnica/TabletOfTheGuilds.java index 9d6141b3a98..b844e1a25c7 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/TabletOfTheGuilds.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/TabletOfTheGuilds.java @@ -138,10 +138,10 @@ class TabletOfTheGuildsGainLifeEffect extends OneShotEffect { ObjectColor color1 = new ObjectColor((String) game.getState().getValue(source.getSourceId() + "_color1")); ObjectColor color2 = new ObjectColor((String) game.getState().getValue(source.getSourceId() + "_color2")); int amount = 0; - if (spell.getColor().contains(color1)) { + if (spell.getColor(game).contains(color1)) { ++amount; } - if (spell.getColor().contains(color2)) { + if (spell.getColor(game).contains(color2)) { ++amount; } if (amount > 0) { diff --git a/Mage.Sets/src/mage/sets/revisededition/WallOfBrambles.java b/Mage.Sets/src/mage/sets/revisededition/WallOfBrambles.java new file mode 100644 index 00000000000..ddc6bc8d1b7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/revisededition/WallOfBrambles.java @@ -0,0 +1,52 @@ +/* + * 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.sets.revisededition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class WallOfBrambles extends mage.sets.limitedalpha.WallOfBrambles { + + public WallOfBrambles(UUID ownerId) { + super(ownerId); + this.cardNumber = 131; + this.expansionSetCode = "3ED"; + } + + public WallOfBrambles(final WallOfBrambles card) { + super(card); + } + + @Override + public WallOfBrambles copy() { + return new WallOfBrambles(this); + } +} diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/AllIsDust.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/AllIsDust.java index d1350b361ba..ab16b725378 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/AllIsDust.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/AllIsDust.java @@ -76,7 +76,7 @@ class AllIsDustEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { List permanents = game.getBattlefield().getActivePermanents(source.getControllerId(), game); for (Permanent p : permanents) { - if (!p.getColor().isColorless()) { + if (!p.getColor(game).isColorless()) { p.sacrifice(source.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/CurseOfWizardry.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/CurseOfWizardry.java index d03c36b7c12..e91deb95bb9 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/CurseOfWizardry.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/CurseOfWizardry.java @@ -131,7 +131,7 @@ class CurseOfWizardryPlayerCastsSpellChosenColorTriggeredAbility extends Trigger if (chosenColor != null) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().shares(chosenColor)) { + if (spell != null && spell.getColor(game).shares(chosenColor)) { this.getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); return true; } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/EldraziTemple.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/EldraziTemple.java index d677a7c819f..07282e367fa 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/EldraziTemple.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/EldraziTemple.java @@ -96,6 +96,6 @@ class EldraziTempleCondition implements Condition { @Override public boolean apply(Game game, Ability source) { MageObject object = game.getObject(source.getSourceId()); - return object != null && object.hasSubtype("Eldrazi") && object.getColor().isColorless(); + return object != null && object.hasSubtype("Eldrazi") && object.getColor(game).isColorless(); } } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/RecurringInsight.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/RecurringInsight.java index 4745f6eef18..564afe4c19a 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/RecurringInsight.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/RecurringInsight.java @@ -49,13 +49,13 @@ public class RecurringInsight extends CardImpl { public RecurringInsight(UUID ownerId) { super(ownerId, 82, "Recurring Insight", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{4}{U}{U}"); this.expansionSetCode = "ROE"; - // Rebound this.addAbility(new ReboundAbility()); // Draw cards equal to the number of cards in target opponent's hand. this.getSpellAbility().addEffect(new RecurringInsightEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); } public RecurringInsight(final RecurringInsight card) { @@ -81,15 +81,13 @@ class RecurringInsightEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - TargetOpponent target = new TargetOpponent(); - Player you = game.getPlayer(source.getControllerId()); - if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) { - you.chooseTarget(Outcome.DrawCard, target, source, game); - Player opponent = game.getPlayer(target.getFirstTarget()); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); if (opponent != null) { - you.drawCards(opponent.getHand().size(), game); - return true; + controller.drawCards(opponent.getHand().size(), game); } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/TajuruPreserver.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/TajuruPreserver.java index f242ffd914d..8baf7705514 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/TajuruPreserver.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/TajuruPreserver.java @@ -40,6 +40,7 @@ import mage.game.permanent.PermanentCard; import mage.game.stack.Spell; import java.util.UUID; +import mage.game.permanent.Permanent; /** * @author noxx @@ -97,15 +98,18 @@ class TajuruPreserverEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - MageObject object = game.getObject(event.getSourceId()); - if (object instanceof PermanentCard) { - if (game.getOpponents(source.getControllerId()).contains(((PermanentCard)object).getControllerId())) { - return true; + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && permanent.getControllerId().equals(source.getControllerId())) { + MageObject object = game.getObject(event.getSourceId()); + if (object instanceof PermanentCard) { + if (game.getOpponents(source.getControllerId()).contains(((PermanentCard)object).getControllerId())) { + return true; + } } - } - if (object instanceof Spell) { - if (game.getOpponents(source.getControllerId()).contains(((Spell)object).getControllerId())) { - return true; + if (object instanceof Spell) { + if (game.getOpponents(source.getControllerId()).contains(((Spell)object).getControllerId())) { + return true; + } } } return false; diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/ZulaportEnforcer.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/ZulaportEnforcer.java index effbf4a5858..a1b8a492f4b 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/ZulaportEnforcer.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/ZulaportEnforcer.java @@ -133,7 +133,7 @@ class ZulaportEnforcerEffect extends RestrictionEffect { @Override public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game) { - if (blocker.getColor().isBlack()) { + if (blocker.getColor(game).isBlack()) { return true; } return false; diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/RendingVines.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/RendingVines.java index 4e59199b067..0716462a951 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/RendingVines.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/RendingVines.java @@ -52,7 +52,6 @@ public class RendingVines extends CardImpl { this.expansionSetCode = "SOK"; this.subtype.add("Arcane"); - // Destroy target artifact or enchantment if its converted mana cost is less than or equal to the number of cards in your hand. this.getSpellAbility().addEffect(new RendingVinesEffect()); this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/YukiOnna.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/YukiOnna.java index 8c9f6a80da0..3c63718db89 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/YukiOnna.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/YukiOnna.java @@ -28,7 +28,6 @@ package mage.sets.saviorsofkamigawa; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; @@ -38,9 +37,8 @@ import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; import mage.filter.common.FilterSpiritOrArcaneCard; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -48,13 +46,6 @@ import mage.target.TargetPermanent; */ public class YukiOnna extends CardImpl { - private static final FilterSpiritOrArcaneCard filter = new FilterSpiritOrArcaneCard(); - private static final FilterPermanent filterTarget = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public YukiOnna(UUID ownerId) { super(ownerId, 120, "Yuki-Onna", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.expansionSetCode = "SOK"; @@ -65,10 +56,10 @@ public class YukiOnna extends CardImpl { // When Yuki-Onna enters the battlefield, destroy target artifact. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - ability.addTarget(new TargetPermanent(filterTarget)); + ability.addTarget(new TargetPermanent(new FilterArtifactPermanent())); this.addAbility(ability); // Whenever you cast a Spirit or Arcane spell, you may return Yuki-Onna to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), filter, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), new FilterSpiritOrArcaneCard(), true)); } public YukiOnna(final YukiOnna card) { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Embersmith.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Embersmith.java index 4982994c785..09d83f6f150 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Embersmith.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Embersmith.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; @@ -40,8 +39,7 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.constants.Outcome; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -51,12 +49,6 @@ import mage.target.common.TargetCreatureOrPlayer; * @author Loki, North */ public class Embersmith extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Embersmith(UUID ownerId) { super(ownerId, 87, "Embersmith", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.expansionSetCode = "SOM"; @@ -66,6 +58,7 @@ public class Embersmith extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); SpellCastControllerTriggeredAbility ability = new SpellCastControllerTriggeredAbility(new EmbersmithEffect(), filter, false); ability.addTarget(new TargetCreatureOrPlayer()); this.addAbility(ability); @@ -79,7 +72,6 @@ public class Embersmith extends CardImpl { public Embersmith copy() { return new Embersmith(this); } - } class EmbersmithEffect extends OneShotEffect { @@ -116,5 +108,4 @@ class EmbersmithEffect extends OneShotEffect { public EmbersmithEffect copy() { return new EmbersmithEffect(this); } - } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Glimmerpost.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Glimmerpost.java index 3fdb0f9a509..39eb17192cd 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Glimmerpost.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Glimmerpost.java @@ -53,8 +53,12 @@ public class Glimmerpost extends CardImpl { super(ownerId, 227, "Glimmerpost", Rarity.COMMON, new CardType[]{CardType.LAND}, null); this.expansionSetCode = "SOM"; this.subtype.add("Locus"); - this.addAbility(new ColorlessManaAbility()); + + // When Glimmerpost enters the battlefield, you gain 1 life for each Locus on the battlefield. this.addAbility(new EntersBattlefieldTriggeredAbility(new GlimmerpostEffect())); + + // {T}: Add to {1} your mana pool. + this.addAbility(new ColorlessManaAbility()); } public Glimmerpost (final Glimmerpost card) { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/GolemFoundry.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/GolemFoundry.java index 59bc3d0c7d2..13d866c247a 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/GolemFoundry.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/GolemFoundry.java @@ -25,14 +25,12 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; @@ -40,26 +38,22 @@ import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.counters.CounterType; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.game.permanent.token.Token; +import mage.filter.common.FilterArtifactSpell; +import mage.game.permanent.token.GolemToken; /** * * @author Loki, North */ public class GolemFoundry extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public GolemFoundry (UUID ownerId) { super(ownerId, 160, "Golem Foundry", Rarity.COMMON, new CardType[]{CardType.ARTIFACT}, "{3}"); this.expansionSetCode = "SOM"; + // Whenever you cast an artifact spell, + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), filter, true)); + // you may put a charge counter on Golem Foundry. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new GolemToken()), new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(3)))); } @@ -71,16 +65,4 @@ public class GolemFoundry extends CardImpl { public GolemFoundry copy() { return new GolemFoundry(this); } - -} - -class GolemToken extends Token { - public GolemToken() { - super("Golem", "a 3/3 colorless Golem artifact creature token"); - cardType.add(CardType.ARTIFACT); - cardType.add(CardType.CREATURE); - subtype.add("Golem"); - power = new MageInt(3); - toughness = new MageInt(3); - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/GrandArchitect.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/GrandArchitect.java index c2e167aebfc..267b4e2852a 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/GrandArchitect.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/GrandArchitect.java @@ -125,11 +125,11 @@ class GrandArchitectEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { - permanent.getColor().setRed(false); - permanent.getColor().setWhite(false); - permanent.getColor().setGreen(false); - permanent.getColor().setBlue(true); - permanent.getColor().setBlack(false); + permanent.getColor(game).setRed(false); + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setGreen(false); + permanent.getColor(game).setBlue(true); + permanent.getColor(game).setBlack(false); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/HaltOrder.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/HaltOrder.java index 43863e366bd..3054ed9722b 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/HaltOrder.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/HaltOrder.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; @@ -34,8 +33,7 @@ import mage.constants.Rarity; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; import mage.target.TargetSpell; /** @@ -43,18 +41,12 @@ import mage.target.TargetSpell; * @author Loki */ public class HaltOrder extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("artifact spell"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public HaltOrder (UUID ownerId) { super(ownerId, 34, "Halt Order", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{U}"); this.expansionSetCode = "SOM"; - this.getSpellAbility().addTarget(new TargetSpell(filter)); + // Counter target artifact spell. Draw a card. + this.getSpellAbility().addTarget(new TargetSpell(new FilterArtifactSpell())); this.getSpellAbility().addEffect(new CounterTargetEffect()); this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); } @@ -67,5 +59,4 @@ public class HaltOrder extends CardImpl { public HaltOrder copy() { return new HaltOrder(this); } - } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/LiegeOfTheTangle.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/LiegeOfTheTangle.java index f17f803ebe9..13089d5f07d 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/LiegeOfTheTangle.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/LiegeOfTheTangle.java @@ -145,7 +145,7 @@ class LiegeOfTheTangleEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - perm.getColor().setColor(token.getColor()); + perm.getColor(game).setColor(token.getColor(game)); } break; case PTChangingEffects_7: diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Lifesmith.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Lifesmith.java index 778fd03b286..ba2a2ca72e3 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Lifesmith.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Lifesmith.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; @@ -40,8 +39,7 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.constants.Outcome; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; import mage.game.Game; import mage.players.Player; @@ -50,12 +48,6 @@ import mage.players.Player; * @author Loki */ public class Lifesmith extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Lifesmith (UUID ownerId) { super(ownerId, 124, "Lifesmith", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.expansionSetCode = "SOM"; @@ -65,6 +57,7 @@ public class Lifesmith extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); this.addAbility(new SpellCastControllerTriggeredAbility(new LifesmithEffect(), filter, false)); } @@ -106,5 +99,4 @@ class LifesmithEffect extends OneShotEffect { public LifesmithEffect copy() { return new LifesmithEffect(this); } - } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Myrsmith.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Myrsmith.java index 29371f63ce1..7e1ebce026c 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Myrsmith.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Myrsmith.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; @@ -40,8 +39,7 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.constants.Outcome; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; import mage.game.Game; import mage.game.permanent.token.MyrToken; @@ -50,12 +48,6 @@ import mage.game.permanent.token.MyrToken; * @author Loki, North */ public class Myrsmith extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Myrsmith (UUID ownerId) { super(ownerId, 16, "Myrsmith", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.expansionSetCode = "SOM"; @@ -65,6 +57,7 @@ public class Myrsmith extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); this.addAbility(new SpellCastControllerTriggeredAbility(new MyrsmithEffect(), filter, false)); } @@ -76,7 +69,6 @@ public class Myrsmith extends CardImpl { public Myrsmith copy() { return new Myrsmith(this); } - } class MyrsmithEffect extends OneShotEffect { @@ -103,5 +95,4 @@ class MyrsmithEffect extends OneShotEffect { public MyrsmithEffect copy() { return new MyrsmithEffect(this); } - } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Painsmith.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Painsmith.java index 1ef30490727..e8e24290811 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Painsmith.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Painsmith.java @@ -38,8 +38,7 @@ import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.cards.CardImpl; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; import mage.target.common.TargetCreaturePermanent; /** @@ -47,12 +46,6 @@ import mage.target.common.TargetCreaturePermanent; * @author Loki, North */ public class Painsmith extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Painsmith (UUID ownerId) { super(ownerId, 74, "Painsmith", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.expansionSetCode = "SOM"; @@ -62,6 +55,7 @@ public class Painsmith extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); SpellCastControllerTriggeredAbility ability = new SpellCastControllerTriggeredAbility(new BoostTargetEffect(2, 0, Duration.EndOfTurn), filter, true); ability.addEffect(new GainAbilityTargetEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn)); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/PrecursorGolem.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/PrecursorGolem.java index 8c00484bf91..0332d1c5eea 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/PrecursorGolem.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/PrecursorGolem.java @@ -46,6 +46,7 @@ import java.util.UUID; import mage.abilities.effects.common.CopySpellForEachItCouldTargetEffect; import mage.filter.FilterInPlay; import mage.filter.common.FilterCreaturePermanent; +import mage.game.permanent.token.GolemToken; import mage.util.TargetAddress; /** @@ -140,7 +141,6 @@ class PrecursorGolemCopyTriggeredAbility extends TriggeredAbilityImpl { } } - class PrecursorGolemCopySpellEffect extends CopySpellForEachItCouldTargetEffect { public PrecursorGolemCopySpellEffect() { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/QuicksilverGargantuan.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/QuicksilverGargantuan.java index 07a5ef4dde3..a02bb9ccda7 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/QuicksilverGargantuan.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/QuicksilverGargantuan.java @@ -93,7 +93,7 @@ public class QuicksilverGargantuan extends CardImpl { Card card = game.getCard(source.getFirstTarget()); Permanent permanent = game.getPermanent(source.getSourceId()); permanent.setName(card.getName()); - permanent.getColor().setColor(card.getColor()); + permanent.getColor(game).setColor(card.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(card.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Riddlesmith.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Riddlesmith.java index 11588e86349..344653885b5 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Riddlesmith.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Riddlesmith.java @@ -35,20 +35,13 @@ import mage.MageInt; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.cards.CardImpl; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; /** * * @author Loki, North */ public class Riddlesmith extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Riddlesmith (UUID ownerId) { super(ownerId, 40, "Riddlesmith", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.expansionSetCode = "SOM"; @@ -58,7 +51,8 @@ public class Riddlesmith extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - this.addAbility(new SpellCastControllerTriggeredAbility(new DrawDiscardControllerEffect(), filter, true)); + // Whenever you cast an artifact spell, you may draw a card. If you do, discard a card. + this.addAbility(new SpellCastControllerTriggeredAbility(new DrawDiscardControllerEffect(), new FilterArtifactSpell("an artifact spell"), true)); } public Riddlesmith (final Riddlesmith card) { @@ -69,5 +63,4 @@ public class Riddlesmith extends CardImpl { public Riddlesmith copy() { return new Riddlesmith(this); } - } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/RustedRelic.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/RustedRelic.java index e6642c03feb..91a9e093543 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/RustedRelic.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/RustedRelic.java @@ -55,7 +55,7 @@ public class RustedRelic extends CardImpl { new ConditionalContinuousEffect( new BecomesCreatureSourceEffect(new RustedRelicToken(), "artifact", Duration.WhileOnBattlefield), MetalcraftCondition.getInstance(), - "Metalcraft - Rusted Relic is a 5/5 Golem artifact creature as long as you control three or more artifacts"))); + "Metalcraft - {this} is a 5/5 Golem artifact creature as long as you control three or more artifacts"))); } public RustedRelic (final RustedRelic card) { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/SliceinTwain.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/SliceinTwain.java index a66f44aa3fa..668c02f32e0 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/SliceinTwain.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/SliceinTwain.java @@ -34,9 +34,7 @@ import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -44,13 +42,6 @@ import mage.target.TargetPermanent; * @author Loki */ public class SliceinTwain extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public SliceinTwain (UUID ownerId) { super(ownerId, 127, "Slice in Twain", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{G}{G}"); @@ -58,7 +49,7 @@ public class SliceinTwain extends CardImpl { this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); } public SliceinTwain (final SliceinTwain card) { @@ -69,5 +60,4 @@ public class SliceinTwain extends CardImpl { public SliceinTwain copy() { return new SliceinTwain(this); } - } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/SteadyProgress.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/SteadyProgress.java index 5da30c9d1c1..2061e03bc81 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/SteadyProgress.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/SteadyProgress.java @@ -29,6 +29,7 @@ package mage.sets.scarsofmirrodin; import java.util.UUID; +import mage.abilities.effects.Effect; import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -45,8 +46,13 @@ public class SteadyProgress extends CardImpl { super(ownerId, 45, "Steady Progress", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{U}"); this.expansionSetCode = "SOM"; + // Proliferate. (You choose any number of permanents and/or players with counters on them, then give each another counter of a kind already there.) this.getSpellAbility().addEffect(new ProliferateEffect()); - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + + // Draw a card. + Effect effect = new DrawCardSourceControllerEffect(1); + effect.setText("
Draw a card"); + this.getSpellAbility().addEffect(effect); } public SteadyProgress (final SteadyProgress card) { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/SunspearShikari.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/SunspearShikari.java index b3f37610ae9..a52650da1c4 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/SunspearShikari.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/SunspearShikari.java @@ -32,6 +32,7 @@ import java.util.UUID; import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.EquippedCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -47,23 +48,23 @@ import mage.constants.Zone; */ public class SunspearShikari extends CardImpl { - private static final String rule1 = "As long as {this} is equipped, it has first strike"; - private static final String rule2 = "As long as {this} is equipped, it has lifelink"; - public SunspearShikari(UUID ownerId) { super(ownerId, 23, "Sunspear Shikari", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.expansionSetCode = "SOM"; this.subtype.add("Cat"); this.subtype.add("Soldier"); - this.color.setWhite(true); this.power = new MageInt(2); this.toughness = new MageInt(2); - ConditionalContinuousEffect effect1 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance()), EquippedCondition.getInstance(), rule1); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect1)); - ConditionalContinuousEffect effect2 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(LifelinkAbility.getInstance()), EquippedCondition.getInstance(), rule2); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect2)); + // As long as Sunspear Shikari is equipped, it has first strike and lifelink. + ConditionalContinuousEffect effect1 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance()), + EquippedCondition.getInstance(), "As long as {this} is equipped, it has first strike"); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect1); + ConditionalContinuousEffect effect2 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(LifelinkAbility.getInstance()), + EquippedCondition.getInstance(), "and lifelink"); + ability.addEffect(effect2); + this.addAbility(ability); } public SunspearShikari(final SunspearShikari card) { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/SylvokReplica.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/SylvokReplica.java index d4f6062842b..20a3e9bd36a 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/SylvokReplica.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/SylvokReplica.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; @@ -39,9 +38,7 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -49,13 +46,6 @@ import mage.target.TargetPermanent; * @author Loki */ public class SylvokReplica extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public SylvokReplica (UUID ownerId) { super(ownerId, 210, "Sylvok Replica", Rarity.COMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); @@ -63,9 +53,11 @@ public class SylvokReplica extends CardImpl { this.subtype.add("Shaman"); this.power = new MageInt(1); this.toughness = new MageInt(3); + + // {G}, Sacrifice Sylvok Replica: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{G}")); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } @@ -77,5 +69,4 @@ public class SylvokReplica extends CardImpl { public SylvokReplica copy() { return new SylvokReplica(this); } - } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/WurmcoilEngine.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/WurmcoilEngine.java index 6bdf90e9803..2a911f2e7f0 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/WurmcoilEngine.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/WurmcoilEngine.java @@ -54,9 +54,11 @@ public class WurmcoilEngine extends CardImpl { this.power = new MageInt(6); this.toughness = new MageInt(6); - + // Deathtouch, lifelink this.addAbility(DeathtouchAbility.getInstance()); this.addAbility(LifelinkAbility.getInstance()); + + // When Wurmcoil Engine dies, put a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink onto the battlefield. Ability ability = new DiesTriggeredAbility(new CreateTokenEffect(new Wurm1Token(expansionSetCode)), false); ability.addEffect(new CreateTokenEffect(new Wurm2Token(expansionSetCode))); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/scourge/BreakAsunder.java b/Mage.Sets/src/mage/sets/scourge/BreakAsunder.java new file mode 100644 index 00000000000..a2772e6d133 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/BreakAsunder.java @@ -0,0 +1,66 @@ +/* + * 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.sets.scourge; + +import java.util.UUID; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.target.TargetPermanent; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class BreakAsunder extends CardImpl { + + public BreakAsunder(UUID ownerId) { + super(ownerId, 113, "Break Asunder", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{2}{G}{G}"); + this.expansionSetCode = "SCG"; + + // Destroy target artifact or enchantment. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + public BreakAsunder(final BreakAsunder card) { + super(card); + } + + @Override + public BreakAsunder copy() { + return new BreakAsunder(this); + } +} diff --git a/Mage.Sets/src/mage/sets/scourge/GoblinBrigand.java b/Mage.Sets/src/mage/sets/scourge/GoblinBrigand.java new file mode 100644 index 00000000000..e82edc3224e --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/GoblinBrigand.java @@ -0,0 +1,52 @@ +/* + * 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.sets.scourge; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class GoblinBrigand extends mage.sets.ninthedition.GoblinBrigand { + + public GoblinBrigand(UUID ownerId) { + super(ownerId); + this.cardNumber = 94; + this.expansionSetCode = "SCG"; + } + + public GoblinBrigand(final GoblinBrigand card) { + super(card); + } + + @Override + public GoblinBrigand copy() { + return new GoblinBrigand(this); + } +} diff --git a/Mage.Sets/src/mage/sets/seventhedition/DarkestHour.java b/Mage.Sets/src/mage/sets/seventhedition/DarkestHour.java index 280425a5827..19022d75324 100644 --- a/Mage.Sets/src/mage/sets/seventhedition/DarkestHour.java +++ b/Mage.Sets/src/mage/sets/seventhedition/DarkestHour.java @@ -83,7 +83,7 @@ class DarkestHourEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { - permanent.getColor().setColor(ObjectColor.BLACK); + permanent.getColor(game).setColor(ObjectColor.BLACK); } return true; } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/CeruleanWisps.java b/Mage.Sets/src/mage/sets/shadowmoor/CeruleanWisps.java index dcecf07ef75..c3b478e635c 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/CeruleanWisps.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/CeruleanWisps.java @@ -54,6 +54,7 @@ public class CeruleanWisps extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new BecomesColorTargetEffect(ObjectColor.BLUE, Duration.EndOfTurn)); this.getSpellAbility().addEffect(new UntapTargetEffect()); + // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/ConsignToDream.java b/Mage.Sets/src/mage/sets/shadowmoor/ConsignToDream.java index 99f3493addf..5375589abe6 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/ConsignToDream.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/ConsignToDream.java @@ -90,8 +90,8 @@ class ConsignToDreamEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent target = game.getPermanent(source.getFirstTarget()); if (target != null) { - if (target.getColor().contains(ObjectColor.RED) - || target.getColor().contains(ObjectColor.GREEN)) { + if (target.getColor(game).contains(ObjectColor.RED) + || target.getColor(game).contains(ObjectColor.GREEN)) { applied = target.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); } else { applied = target.moveToZone(Zone.HAND, source.getSourceId(), game, false); diff --git a/Mage.Sets/src/mage/sets/shadowmoor/FracturingGust.java b/Mage.Sets/src/mage/sets/shadowmoor/FracturingGust.java index b214901a10d..f8b990c0278 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/FracturingGust.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/FracturingGust.java @@ -51,7 +51,6 @@ public class FracturingGust extends CardImpl { super(ownerId, 227, "Fracturing Gust", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{2}{G/W}{G/W}{G/W}"); this.expansionSetCode = "SHM"; - // Destroy all artifacts and enchantments. You gain 2 life for each permanent destroyed this way. this.getSpellAbility().addEffect(new FracturingGustDestroyEffect()); } @@ -94,7 +93,6 @@ class FracturingGustDestroyEffect extends OneShotEffect { for (Permanent permanent: game.getState().getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { if (permanent.destroy(source.getSourceId(), game, false)) { ++destroyedPermanents; - } } game.applyEffects(); // needed in case a destroyed permanent did prevent life gain diff --git a/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java b/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java index e2799927f80..16869be8383 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java @@ -33,9 +33,7 @@ import mage.abilities.keyword.ConspireAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -44,25 +42,16 @@ import mage.target.TargetPermanent; */ public class GleefulSabotage extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("target artifact or enchantment"); - - static { - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), - (new CardTypePredicate(CardType.ENCHANTMENT)))); - } - public GleefulSabotage(UUID ownerId) { super(ownerId, 116, "Gleeful Sabotage", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{1}{G}"); this.expansionSetCode = "SHM"; - // Destroy target artifact or enchantment. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); // Conspire this.addAbility(new ConspireAbility(this)); - } public GleefulSabotage(final GleefulSabotage card) { @@ -74,4 +63,3 @@ public class GleefulSabotage extends CardImpl { return new GleefulSabotage(this); } } - diff --git a/Mage.Sets/src/mage/sets/shadowmoor/Gloomlance.java b/Mage.Sets/src/mage/sets/shadowmoor/Gloomlance.java index 38c20be6e2c..84c70998249 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/Gloomlance.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/Gloomlance.java @@ -90,8 +90,8 @@ class GloomlanceEffect extends OneShotEffect { Player targetController = game.getPlayer(targetCreature.getControllerId()); targetCreature.destroy(source.getSourceId(), game, false); Permanent destroyedCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); - if (destroyedCreature.getColor().isGreen() - || destroyedCreature.getColor().isWhite()) { + if (destroyedCreature.getColor(game).isGreen() + || destroyedCreature.getColor(game).isWhite()) { targetController.discard(1, false, source, game); return true; } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/GloomwidowsFeast.java b/Mage.Sets/src/mage/sets/shadowmoor/GloomwidowsFeast.java index 64695d2f25b..f2b8a590bef 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/GloomwidowsFeast.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/GloomwidowsFeast.java @@ -101,8 +101,8 @@ class GloomwidowsFeastEffect extends OneShotEffect { if (targetCreature != null) { targetCreature.destroy(source.getSourceId(), game, false); Permanent destroyedCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); - if (destroyedCreature.getColor().isBlue() - || destroyedCreature.getColor().isBlack()) { + if (destroyedCreature.getColor(game).isBlue() + || destroyedCreature.getColor(game).isBlack()) { SpiderToken token = new SpiderToken(); token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); return true; diff --git a/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java b/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java index 82b79bb4866..68e79db064f 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java @@ -122,7 +122,7 @@ class TargetTwoCardsWithTheSameColorInHand extends TargetCardInHand { Card chosenCard = cardsToCheck.get(entry.getKey(), game); if (chosenCard != null) { for (UUID cardToCheck : cardsToCheck) { - if (!cardToCheck.equals(chosenCard.getId()) && chosenCard.getColor().equals(game.getCard(cardToCheck).getColor())) { + if (!cardToCheck.equals(chosenCard.getId()) && chosenCard.getColor(game).equals(game.getCard(cardToCheck).getColor(game))) { newPossibleTargets.add(cardToCheck); } } @@ -131,7 +131,7 @@ class TargetTwoCardsWithTheSameColorInHand extends TargetCardInHand { } else { for (UUID cardToCheck : cardsToCheck) { FilterCard colorFilter = new FilterCard(); - colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor())); + colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor(game))); if (cardsToCheck.count(colorFilter, game) > 1) { newPossibleTargets.add(cardToCheck); } @@ -150,7 +150,7 @@ class TargetTwoCardsWithTheSameColorInHand extends TargetCardInHand { int possibleCards = 0; for (UUID cardToCheck : cardsToCheck) { FilterCard colorFilter = new FilterCard(); - colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor())); + colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor(game))); if (cardsToCheck.count(colorFilter, game) > 1) { ++possibleCards; } @@ -165,12 +165,12 @@ class TargetTwoCardsWithTheSameColorInHand extends TargetCardInHand { if (card != null) { if (targets.size() == 1) { Card card2 = game.getCard(targets.entrySet().iterator().next().getKey()); - if (card2 != null && card2.getColor().equals(card.getColor())) { + if (card2 != null && card2.getColor(game).equals(card.getColor(game))) { return true; } } else { FilterCard colorFilter = new FilterCard(); - colorFilter.add(new ColorPredicate(card.getColor())); + colorFilter.add(new ColorPredicate(card.getColor(game))); Player player = game.getPlayer(card.getOwnerId()); if (player.getHand().getCards(colorFilter, game).size() > 1) { return true; diff --git a/Mage.Sets/src/mage/sets/shadowmoor/InquisitorsSnare.java b/Mage.Sets/src/mage/sets/shadowmoor/InquisitorsSnare.java index e32dac76740..6e2d4644565 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/InquisitorsSnare.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/InquisitorsSnare.java @@ -96,8 +96,8 @@ class InquisitorsSnareEffect extends OneShotEffect { FilterCreaturePermanent filter = new FilterCreaturePermanent(); filter.add(new PermanentIdPredicate(targetCreature.getId())); game.addEffect(new PreventAllDamageByAllEffect(filter, Duration.EndOfTurn, false), source); - if (targetCreature.getColor().contains(ObjectColor.BLACK) - || targetCreature.getColor().contains(ObjectColor.RED)) { + if (targetCreature.getColor(game).contains(ObjectColor.BLACK) + || targetCreature.getColor(game).contains(ObjectColor.RED)) { return targetCreature.destroy(source.getSourceId(), game, false); } } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/LureboundScarecrow.java b/Mage.Sets/src/mage/sets/shadowmoor/LureboundScarecrow.java index 68066d70307..0ee0aabd1b4 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/LureboundScarecrow.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/LureboundScarecrow.java @@ -135,7 +135,7 @@ class LureboundScarecrowTriggeredAbility extends StateTriggeredAbility { ObjectColor color = (ObjectColor) game.getState().getValue(card.getId() + "_color"); if (color != null) { for (Permanent perm: game.getBattlefield().getAllActivePermanents(controllerId)) { - if (perm.getColor().contains(color)) + if (perm.getColor(game).contains(color)) return false; } return true; diff --git a/Mage.Sets/src/mage/sets/shadowmoor/OonaQueenOfTheFae.java b/Mage.Sets/src/mage/sets/shadowmoor/OonaQueenOfTheFae.java index 6e783d289df..8a6a73d484f 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/OonaQueenOfTheFae.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/OonaQueenOfTheFae.java @@ -112,7 +112,7 @@ class OonaQueenOfTheFaeEffect extends OneShotEffect { for(int i = 0; i < cardsToExile; i++) { Card card = opponent.getLibrary().removeFromTop(game); if (card != null) { - if (card.getColor().contains(choice.getColor())) { + if (card.getColor(game).contains(choice.getColor())) { cardsWithColor++; } card.moveToExile(null, null, source.getSourceId(), game); diff --git a/Mage.Sets/src/mage/sets/shadowmoor/PaintersServant.java b/Mage.Sets/src/mage/sets/shadowmoor/PaintersServant.java index 18dbad17b8b..10364727e8c 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/PaintersServant.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/PaintersServant.java @@ -135,22 +135,22 @@ class PaintersServantEffect extends ContinuousEffectImpl { } String colorString = color.toString(); for (Permanent perm : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - setObjectColor(perm, colorString); + setObjectColor(perm, colorString, game); } // Stack for (MageObject object : game.getStack()) { if (object instanceof Spell) { - setObjectColor(object, colorString); + setObjectColor(object, colorString, game); } } // Exile for (Card card : game.getExile().getAllCards(game)) { - setObjectColor(card, colorString); + setCardColor(card, colorString, game); } // Command for (CommandObject commandObject : game.getState().getCommand()) { if (commandObject instanceof Commander) { - setObjectColor(commandObject, colorString); + setObjectColor(commandObject, colorString, game); } } @@ -159,15 +159,15 @@ class PaintersServantEffect extends ContinuousEffectImpl { if (player != null) { // Hand for (Card card : player.getHand().getCards(game)) { - setObjectColor(card, colorString); + setCardColor(card, colorString, game); } // Library for (Card card : player.getLibrary().getCards(game)) { - setObjectColor(card, colorString); + setCardColor(card, colorString, game); } // Graveyard for (Card card : player.getGraveyard().getCards(game)) { - setObjectColor(card, colorString); + setCardColor(card, colorString, game); } } } @@ -175,23 +175,44 @@ class PaintersServantEffect extends ContinuousEffectImpl { } return false; } - - protected static void setObjectColor(MageObject obj, String colorString) { - switch (colorString) { + + protected static void setCardColor(Card card, String colorString, Game game) { + ObjectColor color = game.getState().getCreateCardAttribute(card).getColor(); + switch (colorString) { case "W": - obj.getColor().setWhite(true); + color.setWhite(true); break; case "B": - obj.getColor().setBlack(true); + color.setBlack(true); break; case "U": - obj.getColor().setBlue(true); + color.setBlue(true); break; case "G": - obj.getColor().setGreen(true); + color.setGreen(true); break; case "R": - obj.getColor().setRed(true); + color.setRed(true); + break; + } + } + + protected static void setObjectColor(MageObject obj, String colorString, Game game) { + switch (colorString) { + case "W": + obj.getColor(game).setWhite(true); + break; + case "B": + obj.getColor(game).setBlack(true); + break; + case "U": + obj.getColor(game).setBlue(true); + break; + case "G": + obj.getColor(game).setGreen(true); + break; + case "R": + obj.getColor(game).setRed(true); break; } } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/Scrapbasket.java b/Mage.Sets/src/mage/sets/shadowmoor/Scrapbasket.java index 6c14dc47e3b..c590ed08a1c 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/Scrapbasket.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/Scrapbasket.java @@ -92,11 +92,11 @@ class BecomesAllColorsEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { - permanent.getColor().setBlack(true); - permanent.getColor().setBlue(true); - permanent.getColor().setRed(true); - permanent.getColor().setGreen(true); - permanent.getColor().setWhite(true); + permanent.getColor(game).setBlack(true); + permanent.getColor(game).setBlue(true); + permanent.getColor(game).setRed(true); + permanent.getColor(game).setGreen(true); + permanent.getColor(game).setWhite(true); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/shadowmoor/SmashToSmithereens.java b/Mage.Sets/src/mage/sets/shadowmoor/SmashToSmithereens.java index 3214585d64f..98ff75dc2bc 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/SmashToSmithereens.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/SmashToSmithereens.java @@ -51,7 +51,6 @@ public class SmashToSmithereens extends CardImpl { super(ownerId, 107, "Smash to Smithereens", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{R}"); this.expansionSetCode = "SHM"; - // Destroy target artifact. Smash to Smithereens deals 3 damage to that artifact's controller. this.getSpellAbility().addTarget(new TargetArtifactPermanent()); this.getSpellAbility().addEffect(new DestroyTargetEffect()); diff --git a/Mage.Sets/src/mage/sets/shadowmoor/Tyrannize.java b/Mage.Sets/src/mage/sets/shadowmoor/Tyrannize.java index d075c7ddfad..cc9985ed412 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/Tyrannize.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/Tyrannize.java @@ -92,7 +92,7 @@ class TyrannizeEffect extends OneShotEffect { Cost cost = new PayLifeCost(7); if (!cost.canPay(source, source.getSourceId(), player.getId(), game) || !player.chooseUse(Outcome.LoseLife, "Pay 7 life?", game) - || !cost.pay(source, game, source.getSourceId(), player.getId(), true)) { + || !cost.pay(source, game, source.getSourceId(), player.getId(), false)) { for (Card card : player.getHand().getCards(game)) { player.discard(card, source, game); } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/DispellersCapsule.java b/Mage.Sets/src/mage/sets/shardsofalara/DispellersCapsule.java index 29d1688d48d..74a8c8d8471 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/DispellersCapsule.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/DispellersCapsule.java @@ -25,11 +25,9 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.shardsofalara; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; @@ -39,9 +37,7 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -49,21 +45,14 @@ import mage.target.TargetPermanent; * @author Loki */ public class DispellersCapsule extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - + public DispellersCapsule (UUID ownerId) { super(ownerId, 8, "Dispeller's Capsule", Rarity.COMMON, new CardType[]{CardType.ARTIFACT}, "{W}"); this.expansionSetCode = "ALA"; Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{2}{W}")); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/RelicOfProgenitus.java b/Mage.Sets/src/mage/sets/shardsofalara/RelicOfProgenitus.java index 49e54fd61bc..2b967b40bac 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/RelicOfProgenitus.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/RelicOfProgenitus.java @@ -35,6 +35,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -63,11 +64,10 @@ public class RelicOfProgenitus extends CardImpl { firstAbility.addTarget(new TargetPlayer()); this.addAbility(firstAbility); // {1}, Exile Relic of Progenitus: Exile all cards from all graveyards. Draw a card. - Ability secondAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RelicOfProgenitusEffect2(),new GenericManaCost(1)); + Ability secondAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileGraveyardAllPlayersEffect(),new GenericManaCost(1)); secondAbility.addCost(new ExileSourceCost()); secondAbility.addEffect(new DrawCardSourceControllerEffect(1)); this.addAbility(secondAbility); - } public RelicOfProgenitus(final RelicOfProgenitus card) { @@ -114,41 +114,3 @@ class RelicOfProgenitusEffect extends OneShotEffect { return false; } } - -class RelicOfProgenitusEffect2 extends OneShotEffect { - - public RelicOfProgenitusEffect2() { - super(Outcome.Detriment); - staticText = "Exile all cards from all graveyards"; - } - - public RelicOfProgenitusEffect2(final RelicOfProgenitusEffect2 effect) { - super(effect); - } - - @Override - public RelicOfProgenitusEffect2 copy() { - return new RelicOfProgenitusEffect2(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - - for (UUID playerId : controller.getInRange()) { - Player player = game.getPlayer(playerId); - if (player != null) { - for (UUID cid : player.getGraveyard().copy()) { - Card card = game.getCard(cid); - if (card != null) { - controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.GRAVEYARD, true); - } - } - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/sets/tempest/DeathPitsOfRath.java b/Mage.Sets/src/mage/sets/tempest/DeathPitsOfRath.java index f1c4a314bfa..1af8d3cb656 100644 --- a/Mage.Sets/src/mage/sets/tempest/DeathPitsOfRath.java +++ b/Mage.Sets/src/mage/sets/tempest/DeathPitsOfRath.java @@ -50,7 +50,6 @@ public class DeathPitsOfRath extends CardImpl { super(ownerId, 21, "Death Pits of Rath", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}"); this.expansionSetCode = "TMP"; - // Whenever a creature is dealt damage, destroy it. It can't be regenerated. this.addAbility(new DeathPitsOfRathTriggeredAbility()); } @@ -80,16 +79,17 @@ class DeathPitsOfRathTriggeredAbility extends TriggeredAbilityImpl { return new DeathPitsOfRathTriggeredAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE) { - for(Effect effect : this.getEffects()) - { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - } - return true; + for(Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getTargetId())); } - return false; + return true; } @Override diff --git a/Mage.Sets/src/mage/sets/tempest/DiabolicEdict.java b/Mage.Sets/src/mage/sets/tempest/DiabolicEdict.java index 638f4e0b470..a6ad0c8a3c7 100644 --- a/Mage.Sets/src/mage/sets/tempest/DiabolicEdict.java +++ b/Mage.Sets/src/mage/sets/tempest/DiabolicEdict.java @@ -45,6 +45,7 @@ public class DiabolicEdict extends CardImpl { super(ownerId, 22, "Diabolic Edict", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{B}"); this.expansionSetCode = "TMP"; + // Target player sacrifices a creature. this.getSpellAbility().addEffect(new SacrificeEffect(new FilterCreaturePermanent(), 1, "Target player")); this.getSpellAbility().addTarget(new TargetPlayer()); } diff --git a/Mage.Sets/src/mage/sets/tempest/Disenchant.java b/Mage.Sets/src/mage/sets/tempest/Disenchant.java index 1a217e67c26..2d65f90508b 100644 --- a/Mage.Sets/src/mage/sets/tempest/Disenchant.java +++ b/Mage.Sets/src/mage/sets/tempest/Disenchant.java @@ -32,9 +32,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -43,20 +41,13 @@ import mage.target.TargetPermanent; */ public class Disenchant extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public Disenchant(UUID ownerId) { super(ownerId, 228, "Disenchant", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{W}"); this.expansionSetCode = "TMP"; + // Destroy target artifact or enchantment. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); } public Disenchant(final Disenchant card) { diff --git a/Mage.Sets/src/mage/sets/tempest/Grindstone.java b/Mage.Sets/src/mage/sets/tempest/Grindstone.java index aac4ee1fec2..f2b73cbca18 100644 --- a/Mage.Sets/src/mage/sets/tempest/Grindstone.java +++ b/Mage.Sets/src/mage/sets/tempest/Grindstone.java @@ -114,8 +114,8 @@ class GrindstoneEffect extends OneShotEffect { Card card1 = targetPlayer.getLibrary().removeFromTop(game); if (targetPlayer.getLibrary().size() > 0) { Card card2 = targetPlayer.getLibrary().removeFromTop(game); - if (card1.getColor().hasColor() && card2.getColor().hasColor()) { - colorShared = card1.getColor().shares(card2.getColor()); + if (card1.getColor(game).hasColor() && card2.getColor(game).hasColor()) { + colorShared = card1.getColor(game).shares(card2.getColor(game)); } } } diff --git a/Mage.Sets/src/mage/sets/tempest/HelmOfPossession.java b/Mage.Sets/src/mage/sets/tempest/HelmOfPossession.java index 7619182d725..f80c7843013 100644 --- a/Mage.Sets/src/mage/sets/tempest/HelmOfPossession.java +++ b/Mage.Sets/src/mage/sets/tempest/HelmOfPossession.java @@ -34,6 +34,7 @@ import mage.abilities.common.SkipUntapOptionalAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; @@ -64,7 +65,8 @@ public class HelmOfPossession extends CardImpl { new GainControlTargetEffect(Duration.Custom), new HelmOfPossessionCondition(), "Gain control of target creature for as long as you control {this} and {this} remains tapped"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/tempest/LightOfDay.java b/Mage.Sets/src/mage/sets/tempest/LightOfDay.java index 8924dcf50bb..17348a8cb6d 100644 --- a/Mage.Sets/src/mage/sets/tempest/LightOfDay.java +++ b/Mage.Sets/src/mage/sets/tempest/LightOfDay.java @@ -95,7 +95,7 @@ class LightOfDayEffect extends ReplacementEffectImpl { if (permanent != null) { Player player = game.getPlayer(source.getControllerId()); if (player.getInRange().contains(permanent.getControllerId())) { - if (permanent.getColor().isBlack()) { + if (permanent.getColor(game).isBlack()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/tempest/Verdigris.java b/Mage.Sets/src/mage/sets/tempest/Verdigris.java index dbcf4169c3d..d0ceab23f2f 100644 --- a/Mage.Sets/src/mage/sets/tempest/Verdigris.java +++ b/Mage.Sets/src/mage/sets/tempest/Verdigris.java @@ -32,8 +32,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -42,18 +41,12 @@ import mage.target.TargetPermanent; */ public class Verdigris extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Verdigris(UUID ownerId) { super(ownerId, 158, "Verdigris", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{G}"); this.expansionSetCode = "TMP"; this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); } public Verdigris(final Verdigris card) { diff --git a/Mage.Sets/src/mage/sets/tenthedition/AngelsFeather.java b/Mage.Sets/src/mage/sets/tenthedition/AngelsFeather.java index 88610167c3d..2d01c26e37f 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/AngelsFeather.java +++ b/Mage.Sets/src/mage/sets/tenthedition/AngelsFeather.java @@ -82,7 +82,7 @@ class AngelsFeatherAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isWhite()) { + if (spell != null && spell.getColor(game).isWhite()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/tenthedition/Clone.java b/Mage.Sets/src/mage/sets/tenthedition/Clone.java index 2284d16afac..2412dacd079 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/Clone.java +++ b/Mage.Sets/src/mage/sets/tenthedition/Clone.java @@ -53,6 +53,7 @@ public class Clone extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(0); + // You may have Clone enter the battlefield as a copy of any creature on the battlefield. Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new EntersBattlefieldEffect( new CopyPermanentEffect(), "You may have {this} enter the battlefield as a copy of any creature on the battlefield", diff --git a/Mage.Sets/src/mage/sets/tenthedition/DemonsHorn.java b/Mage.Sets/src/mage/sets/tenthedition/DemonsHorn.java index acea56e7df1..14a17bd3172 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/DemonsHorn.java +++ b/Mage.Sets/src/mage/sets/tenthedition/DemonsHorn.java @@ -29,16 +29,15 @@ package mage.sets.tenthedition; import java.util.UUID; +import mage.ObjectColor; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.Zone; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.stack.Spell; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ColorPredicate; /** * @@ -46,10 +45,18 @@ import mage.game.stack.Spell; */ public class DemonsHorn extends CardImpl { + private final static FilterSpell filter = new FilterSpell("a black spell"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLACK)); + } + public DemonsHorn(UUID ownerId) { super(ownerId, 320, "Demon's Horn", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{2}"); this.expansionSetCode = "10E"; - this.addAbility(new DemonsHornAbility()); + + // Whenever a player casts a black spell, you may gain 1 life. + this.addAbility(new SpellCastAllTriggeredAbility(new GainLifeEffect(new StaticValue(1), "you may gain 1 life"), filter, true)); } public DemonsHorn(final DemonsHorn card) { @@ -62,36 +69,3 @@ public class DemonsHorn extends CardImpl { } } - -class DemonsHornAbility extends TriggeredAbilityImpl { - - public DemonsHornAbility() { - super(Zone.BATTLEFIELD, new GainLifeEffect(1), true); - } - - public DemonsHornAbility(final DemonsHornAbility ability) { - super(ability); - } - - @Override - public DemonsHornAbility copy() { - return new DemonsHornAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == EventType.SPELL_CAST) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isBlack()) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a player casts a black spell, you may gain 1 life."; - } - -} diff --git a/Mage.Sets/src/mage/sets/tenthedition/DragonsClaw.java b/Mage.Sets/src/mage/sets/tenthedition/DragonsClaw.java index ec8e5916f51..86a28f2dd3c 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/DragonsClaw.java +++ b/Mage.Sets/src/mage/sets/tenthedition/DragonsClaw.java @@ -78,15 +78,15 @@ class DragonsClawAbility extends TriggeredAbilityImpl { return new DragonsClawAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.SPELL_CAST; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == EventType.SPELL_CAST) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isRed()) { - return true; - } - } - return false; + Spell spell = game.getStack().getSpell(event.getTargetId()); + return spell != null && spell.getColor(game).isRed(); } @Override diff --git a/Mage.Sets/src/mage/sets/tenthedition/KrakensEye.java b/Mage.Sets/src/mage/sets/tenthedition/KrakensEye.java index 9a81eacf97d..81e51ec0539 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/KrakensEye.java +++ b/Mage.Sets/src/mage/sets/tenthedition/KrakensEye.java @@ -82,7 +82,7 @@ class KrakensEyeAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isBlue()) { + if (spell != null && spell.getColor(game).isBlue()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/tenthedition/Naturalize.java b/Mage.Sets/src/mage/sets/tenthedition/Naturalize.java index 3476eec9d73..7caac8fd17b 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/Naturalize.java +++ b/Mage.Sets/src/mage/sets/tenthedition/Naturalize.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.tenthedition; import java.util.UUID; @@ -33,9 +32,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -44,19 +41,12 @@ import mage.target.TargetPermanent; */ public class Naturalize extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public Naturalize(UUID ownerId){ super(ownerId, 282, "Naturalize", Rarity.COMMON, new CardType[]{CardType.INSTANT},"{1}{G}"); this.expansionSetCode = "10E"; - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + // Destroy target artifact or enchantment. + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/sets/tenthedition/Nightmare.java b/Mage.Sets/src/mage/sets/tenthedition/Nightmare.java index 42e0b57e4f9..57a192538c7 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/Nightmare.java +++ b/Mage.Sets/src/mage/sets/tenthedition/Nightmare.java @@ -61,7 +61,11 @@ public class Nightmare extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(0); + + // Flying this.addAbility(FlyingAbility.getInstance()); + + // Nightmare's power and toughness are each equal to the number of Swamps you control. this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new PermanentsOnBattlefieldCount(filter), Duration.EndOfGame))); } diff --git a/Mage.Sets/src/mage/sets/tenthedition/Peek.java b/Mage.Sets/src/mage/sets/tenthedition/Peek.java index 1ef7f16dbef..7f60d71d82a 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/Peek.java +++ b/Mage.Sets/src/mage/sets/tenthedition/Peek.java @@ -28,6 +28,7 @@ package mage.sets.tenthedition; import java.util.UUID; +import mage.MageObject; import mage.constants.CardType; import mage.constants.Rarity; @@ -50,10 +51,10 @@ public class Peek extends CardImpl { super(ownerId, 94, "Peek", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{U}"); this.expansionSetCode = "10E"; - // Look at target player's hand. this.getSpellAbility().addEffect(new PeekEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); + // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); } @@ -71,7 +72,7 @@ public class Peek extends CardImpl { class PeekEffect extends OneShotEffect { PeekEffect() { super(Outcome.Detriment); - staticText = "Look at target player's hand"; + staticText = "look at target player's hand"; } PeekEffect(final PeekEffect effect) { @@ -82,8 +83,9 @@ class PeekEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null && controller != null) { - controller.lookAtCards("Peek", player.getHand(), game); + MageObject sourceObject = source.getSourceObject(game); + if (player != null && controller != null && sourceObject != null) { + controller.lookAtCards(sourceObject.getIdName(), player.getHand(), game); } return true; } diff --git a/Mage.Sets/src/mage/sets/tenthedition/WurmsTooth.java b/Mage.Sets/src/mage/sets/tenthedition/WurmsTooth.java index d211eb06b33..31b2ff02519 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/WurmsTooth.java +++ b/Mage.Sets/src/mage/sets/tenthedition/WurmsTooth.java @@ -82,7 +82,7 @@ class WurmsToothAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isGreen()) { + if (spell != null && spell.getColor(game).isGreen()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/thedark/Fissure.java b/Mage.Sets/src/mage/sets/thedark/Fissure.java new file mode 100644 index 00000000000..63f5d958355 --- /dev/null +++ b/Mage.Sets/src/mage/sets/thedark/Fissure.java @@ -0,0 +1,70 @@ +/* + * 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.sets.thedark; + +import java.util.UUID; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.target.Target; +import mage.target.TargetPermanent; + +/** + * + * @author Jgod + */ +public class Fissure extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("creature or land"); + static { + filter.add(Predicates.or(new CardTypePredicate(CardType.CREATURE), new CardTypePredicate(CardType.LAND))); + } + + public Fissure(UUID ownerId) { + super(ownerId, 62, "Fissure", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{3}{R}{R}"); + this.expansionSetCode = "DRK"; + + // Destroy target creature or land. It can't be regenerated. + this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); + Target target = new TargetPermanent(filter); + this.getSpellAbility().addTarget(target); + } + + public Fissure(final Fissure card) { + super(card); + } + + @Override + public Fissure copy() { + return new Fissure(this); + } +} + diff --git a/Mage.Sets/src/mage/sets/theros/ArtisansSorrow.java b/Mage.Sets/src/mage/sets/theros/ArtisansSorrow.java index 650dfe91f86..c24bc93531d 100644 --- a/Mage.Sets/src/mage/sets/theros/ArtisansSorrow.java +++ b/Mage.Sets/src/mage/sets/theros/ArtisansSorrow.java @@ -33,9 +33,7 @@ import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -43,20 +41,13 @@ import mage.target.TargetPermanent; * @author LevelX2 */ public class ArtisansSorrow extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - static { - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ENCHANTMENT))); - } - public ArtisansSorrow(UUID ownerId) { super(ownerId, 151, "Artisan's Sorrow", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{3}{G}"); this.expansionSetCode = "THS"; - // Destroy target artifact or enchantment. Scry 2. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new ScryEffect(2)); } diff --git a/Mage.Sets/src/mage/sets/theros/DestructiveRevelry.java b/Mage.Sets/src/mage/sets/theros/DestructiveRevelry.java index 9bc8aeda091..88cb8a54b53 100644 --- a/Mage.Sets/src/mage/sets/theros/DestructiveRevelry.java +++ b/Mage.Sets/src/mage/sets/theros/DestructiveRevelry.java @@ -34,9 +34,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -49,19 +47,13 @@ import mage.target.TargetPermanent; */ public class DestructiveRevelry extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - static { - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ENCHANTMENT))); - } - public DestructiveRevelry(UUID ownerId) { super(ownerId, 192, "Destructive Revelry", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{R}{G}"); this.expansionSetCode = "THS"; - // Destroy target artifact or enchantment. Destructive Revelry deals 2 damage to that permanent's controller. this.getSpellAbility().addEffect(new DestructiveRevelryEffect()); - Target target = new TargetPermanent(filter); + Target target = new TargetPermanent(new FilterArtifactOrEnchantmentPermanent()); this.getSpellAbility().addTarget(target); } diff --git a/Mage.Sets/src/mage/sets/theros/GodsWilling.java b/Mage.Sets/src/mage/sets/theros/GodsWilling.java index 821ed08b709..f2bfc0b233b 100644 --- a/Mage.Sets/src/mage/sets/theros/GodsWilling.java +++ b/Mage.Sets/src/mage/sets/theros/GodsWilling.java @@ -46,7 +46,6 @@ public class GodsWilling extends CardImpl { super(ownerId, 16, "Gods Willing", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{W}"); this.expansionSetCode = "THS"; - // Target creature you control gains protection from the color of your choice until end of turn. Scry 1. this.getSpellAbility().addEffect(new GainProtectionFromColorTargetEffect(Duration.EndOfTurn)); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); diff --git a/Mage.Sets/src/mage/sets/theros/HammerOfPurphoros.java b/Mage.Sets/src/mage/sets/theros/HammerOfPurphoros.java index 640ee18e60b..15d29e26ed8 100644 --- a/Mage.Sets/src/mage/sets/theros/HammerOfPurphoros.java +++ b/Mage.Sets/src/mage/sets/theros/HammerOfPurphoros.java @@ -45,7 +45,6 @@ import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.game.permanent.token.SaprolingToken; import mage.game.permanent.token.Token; import mage.target.common.TargetControlledPermanent; @@ -60,10 +59,10 @@ public class HammerOfPurphoros extends CardImpl { this.expansionSetCode = "THS"; this.supertype.add("Legendary"); - // Creatures you control have haste. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, new FilterCreaturePermanent("Creatures")))); + // {2}{R}, {tap}, Sacrifice a land: Put a 3/3 colorless Golem enchantment artifact creature token onto the battlefield. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new HammerOfPurphorosGolemToken()), new ManaCostsImpl("{2}{R}")); ability.addCost(new TapSourceCost()); @@ -84,6 +83,7 @@ class HammerOfPurphorosGolemToken extends Token { public HammerOfPurphorosGolemToken() { super("Golem", "3/3 colorless Golem enchantment artifact creature token"); + setOriginalExpansionSetCode("THS"); cardType.add(CardType.ENCHANTMENT); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); diff --git a/Mage.Sets/src/mage/sets/theros/OrdealOfHeliod.java b/Mage.Sets/src/mage/sets/theros/OrdealOfHeliod.java index 19bda410efa..cb280e33305 100644 --- a/Mage.Sets/src/mage/sets/theros/OrdealOfHeliod.java +++ b/Mage.Sets/src/mage/sets/theros/OrdealOfHeliod.java @@ -58,7 +58,6 @@ public class OrdealOfHeliod extends CardImpl { this.expansionSetCode = "THS"; this.subtype.add("Aura"); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); diff --git a/Mage.Sets/src/mage/sets/timeshifted/SolkanarTheSwampKing.java b/Mage.Sets/src/mage/sets/timeshifted/SolkanarTheSwampKing.java index d0d9f4bbeb3..282bb67dd00 100644 --- a/Mage.Sets/src/mage/sets/timeshifted/SolkanarTheSwampKing.java +++ b/Mage.Sets/src/mage/sets/timeshifted/SolkanarTheSwampKing.java @@ -91,7 +91,7 @@ class SolkanarTheSwampKingAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isBlack()) { + if (spell != null && spell.getColor(game).isBlack()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/timespiral/AncestralVision.java b/Mage.Sets/src/mage/sets/timespiral/AncestralVision.java index f3ef3a34260..08a51389a09 100644 --- a/Mage.Sets/src/mage/sets/timespiral/AncestralVision.java +++ b/Mage.Sets/src/mage/sets/timespiral/AncestralVision.java @@ -46,7 +46,7 @@ public class AncestralVision extends CardImpl { super(ownerId, 48, "Ancestral Vision", Rarity.RARE, new CardType[]{CardType.SORCERY}, ""); this.expansionSetCode = "TSP"; - this.getColor().setBlue(true); + this.color.setBlue(true); // Suspend 4-{U} this.addAbility(new SuspendAbility(4, new ManaCostsImpl("U"), this)); diff --git a/Mage.Sets/src/mage/sets/timespiral/AngelsGrace.java b/Mage.Sets/src/mage/sets/timespiral/AngelsGrace.java index f51413dd1f5..3a7aef912e8 100644 --- a/Mage.Sets/src/mage/sets/timespiral/AngelsGrace.java +++ b/Mage.Sets/src/mage/sets/timespiral/AngelsGrace.java @@ -53,7 +53,7 @@ public class AngelsGrace extends CardImpl { this.expansionSetCode = "TSP"; - // Split second + // Split second (As long as this spell is on the stack, players can't cast spells or activate abilities that aren't mana abilities.) this.addAbility(new SplitSecondAbility()); // You can't lose the game this turn and your opponents can't win the game this turn. Until end of turn, damage that would reduce your life total to less than 1 reduces it to 1 instead. diff --git a/Mage.Sets/src/mage/sets/timespiral/GauntletOfPower.java b/Mage.Sets/src/mage/sets/timespiral/GauntletOfPower.java index fec94eac07e..9cbcd3812c5 100644 --- a/Mage.Sets/src/mage/sets/timespiral/GauntletOfPower.java +++ b/Mage.Sets/src/mage/sets/timespiral/GauntletOfPower.java @@ -116,7 +116,7 @@ class GauntletOfPowerEffect1 extends ContinuousEffectImpl { ObjectColor color = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color"); if (color != null) { for (Permanent perm: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (perm.getColor().contains(color)) { + if (perm.getColor(game).contains(color)) { perm.addPower(1); perm.addToughness(1); } diff --git a/Mage.Sets/src/mage/sets/timespiral/GhostflameSliver.java b/Mage.Sets/src/mage/sets/timespiral/GhostflameSliver.java index 7b1e4be27d7..ffbf3229a90 100644 --- a/Mage.Sets/src/mage/sets/timespiral/GhostflameSliver.java +++ b/Mage.Sets/src/mage/sets/timespiral/GhostflameSliver.java @@ -87,7 +87,7 @@ class GhostflameSliverEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { for (Permanent perm : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { if (filter.match(perm, game)) { - perm.getColor().setColor(colorless); + perm.getColor(game).setColor(colorless); } } return true; diff --git a/Mage.Sets/src/mage/sets/timespiral/HarmonicSliver.java b/Mage.Sets/src/mage/sets/timespiral/HarmonicSliver.java index fef56e3baa3..bfee4df16fd 100644 --- a/Mage.Sets/src/mage/sets/timespiral/HarmonicSliver.java +++ b/Mage.Sets/src/mage/sets/timespiral/HarmonicSliver.java @@ -39,10 +39,8 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.TargetPermanent; @@ -53,13 +51,8 @@ import mage.target.TargetPermanent; public class HarmonicSliver extends CardImpl { private static final FilterCreaturePermanent filterSliver = new FilterCreaturePermanent(); - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); static { filterSliver.add(new SubtypePredicate("Sliver")); - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public HarmonicSliver(UUID ownerId) { @@ -72,7 +65,7 @@ public class HarmonicSliver extends CardImpl { // All Slivers have "When this permanent enters the battlefield, destroy target artifact or enchantment." Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - TargetPermanent target = new TargetPermanent(filter); + TargetPermanent target = new TargetPermanent(new FilterArtifactOrEnchantmentPermanent()); ability.addTarget(target); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( ability, Duration.WhileOnBattlefield, diff --git a/Mage.Sets/src/mage/sets/timespiral/KrosanGrip.java b/Mage.Sets/src/mage/sets/timespiral/KrosanGrip.java index 8c434a31c1b..9be46756220 100644 --- a/Mage.Sets/src/mage/sets/timespiral/KrosanGrip.java +++ b/Mage.Sets/src/mage/sets/timespiral/KrosanGrip.java @@ -33,9 +33,7 @@ import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.SplitSecondAbility; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -43,24 +41,15 @@ import mage.target.TargetPermanent; * @author jonubuu */ public class KrosanGrip extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public KrosanGrip(UUID ownerId) { super(ownerId, 202, "Krosan Grip", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{G}"); this.expansionSetCode = "TSP"; - // Split second this.addAbility(new SplitSecondAbility()); // Destroy target artifact or enchantment. - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/sets/timespiral/MightOfOldKrosa.java b/Mage.Sets/src/mage/sets/timespiral/MightOfOldKrosa.java index eb4fac5aba9..cf4d66310b8 100644 --- a/Mage.Sets/src/mage/sets/timespiral/MightOfOldKrosa.java +++ b/Mage.Sets/src/mage/sets/timespiral/MightOfOldKrosa.java @@ -48,7 +48,6 @@ public class MightOfOldKrosa extends CardImpl { super(ownerId, 204, "Might of Old Krosa", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{G}"); this.expansionSetCode = "TSP"; - // Target creature gets +2/+2 until end of turn. If you cast this spell during your main phase, that creature gets +4/+4 until end of turn instead. this.getSpellAbility().addEffect(new ConditionalContinuousEffect( new BoostTargetEffect(4,4, Duration.EndOfTurn), diff --git a/Mage.Sets/src/mage/sets/timespiral/TeferiMageOfZhalfir.java b/Mage.Sets/src/mage/sets/timespiral/TeferiMageOfZhalfir.java index e545ced4af1..12ce53027ff 100644 --- a/Mage.Sets/src/mage/sets/timespiral/TeferiMageOfZhalfir.java +++ b/Mage.Sets/src/mage/sets/timespiral/TeferiMageOfZhalfir.java @@ -126,7 +126,12 @@ class TeferiMageOfZhalfirAddFlashEffect extends ContinuousEffectImpl { game.getState().addOtherAbility(card, FlashAbility.getInstance()); } } - // in Library seems not relevant yet + // in Library (e.g. for Mystical Teachings) + for (Card card: controller.getLibrary().getCards(game)) { + if (card.getOwnerId().equals(controller.getId()) && card.getCardType().contains(CardType.CREATURE)) { + game.getState().addOtherAbility(card, FlashAbility.getInstance()); + } + } return true; } return false; diff --git a/Mage.Sets/src/mage/sets/torment/Compulsion.java b/Mage.Sets/src/mage/sets/torment/Compulsion.java new file mode 100644 index 00000000000..e2f7d90169e --- /dev/null +++ b/Mage.Sets/src/mage/sets/torment/Compulsion.java @@ -0,0 +1,73 @@ +/* + * 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.sets.torment; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.filter.FilterCard; +import mage.target.common.TargetCardInHand; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class Compulsion extends CardImpl { + + public Compulsion(UUID ownerId) { + super(ownerId, 34, "Compulsion", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + this.expansionSetCode = "TOR"; + + // {1}{U}, Discard a card: Draw a card. + Ability ability1 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}{U}")); + ability1.addCost(new DiscardTargetCost(new TargetCardInHand(1, new FilterCard("a card")))); + this.addAbility(ability1); + + // {1}{U}, Sacrifice Compulsion: Draw a card. + Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}{U}")); + ability2.addCost(new SacrificeSourceCost()); + this.addAbility(ability2); + } + + public Compulsion(final Compulsion card) { + super(card); + } + + @Override + public Compulsion copy() { + return new Compulsion(this); + } +} diff --git a/Mage.Sets/src/mage/sets/torment/Morningtide.java b/Mage.Sets/src/mage/sets/torment/Morningtide.java new file mode 100644 index 00000000000..cc699128a42 --- /dev/null +++ b/Mage.Sets/src/mage/sets/torment/Morningtide.java @@ -0,0 +1,58 @@ +/* + * 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.sets.torment; + +import java.util.UUID; +import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class Morningtide extends CardImpl { + + public Morningtide(UUID ownerId) { + super(ownerId, 10, "Morningtide", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{W}"); + this.expansionSetCode = "TOR"; + + // Exile all cards from all graveyards. + this.getSpellAbility().addEffect(new ExileGraveyardAllPlayersEffect()); + } + + public Morningtide(final Morningtide card) { + super(card); + } + + @Override + public Morningtide copy() { + return new Morningtide(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/unlimitededition/Forcefield.java b/Mage.Sets/src/mage/sets/unlimitededition/Forcefield.java new file mode 100644 index 00000000000..f91d69dfe09 --- /dev/null +++ b/Mage.Sets/src/mage/sets/unlimitededition/Forcefield.java @@ -0,0 +1,52 @@ +/* + * 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.sets.unlimitededition; + +import java.util.UUID; + +/** + * + * @author emerald000 + */ +public class Forcefield extends mage.sets.mastersedition.Forcefield { + + public Forcefield(UUID ownerId) { + super(ownerId); + this.cardNumber = 244; + this.expansionSetCode = "2ED"; + } + + public Forcefield(final Forcefield card) { + super(card); + } + + @Override + public Forcefield copy() { + return new Forcefield(this); + } +} diff --git a/Mage.Sets/src/mage/sets/unlimitededition/WallOfBrambles.java b/Mage.Sets/src/mage/sets/unlimitededition/WallOfBrambles.java new file mode 100644 index 00000000000..a64740c9a1a --- /dev/null +++ b/Mage.Sets/src/mage/sets/unlimitededition/WallOfBrambles.java @@ -0,0 +1,52 @@ +/* + * 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.sets.unlimitededition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class WallOfBrambles extends mage.sets.limitedalpha.WallOfBrambles { + + public WallOfBrambles(UUID ownerId) { + super(ownerId); + this.cardNumber = 132; + this.expansionSetCode = "2ED"; + } + + public WallOfBrambles(final WallOfBrambles card) { + super(card); + } + + @Override + public WallOfBrambles copy() { + return new WallOfBrambles(this); + } +} diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/BloodshotCyclops.java b/Mage.Sets/src/mage/sets/urzasdestiny/BloodshotCyclops.java index 3a9f747a28c..1fa8d58f38f 100644 --- a/Mage.Sets/src/mage/sets/urzasdestiny/BloodshotCyclops.java +++ b/Mage.Sets/src/mage/sets/urzasdestiny/BloodshotCyclops.java @@ -37,6 +37,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.game.Game; @@ -62,7 +63,8 @@ public class BloodshotCyclops extends CardImpl { // {T}, Sacrifice a creature: Bloodshot Cyclops deals damage equal to the sacrificed // creature's power to target creature or player. - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BloodshotCyclopsEffect(), new SacrificeTargetCost(new TargetControlledCreaturePermanent())); + SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BloodshotCyclopsEffect(), new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent())); ability.addTarget(new TargetCreatureOrPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java b/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java new file mode 100644 index 00000000000..a1018936db2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java @@ -0,0 +1,142 @@ +/* + * 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.sets.urzasdestiny; + +import java.util.UUID; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; + +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author cbrianhill + */ +public class Repercussion extends CardImpl { + + public Repercussion(UUID ownerId) { + super(ownerId, 95, "Repercussion", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}{R}"); + this.expansionSetCode = "UDS"; + + // Whenever a creature is dealt damage, Repercussion deals that much damage to that creature's controller. + this.addAbility(new RepercussionTriggeredAbility(new RepercussionEffect())); + } + + public Repercussion(final Repercussion card) { + super(card); + } + + @Override + public Repercussion copy() { + return new Repercussion(this); + } +} + +class RepercussionTriggeredAbility extends TriggeredAbilityImpl { + + static final String PLAYER_DAMAGE_AMOUNT_KEY = "playerDamage"; + static final String TRIGGERING_CREATURE_KEY = "triggeringCreature"; + + public RepercussionTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect); + } + + public RepercussionTriggeredAbility(final RepercussionTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + for(Effect effect : getEffects()) { + effect.setValue(PLAYER_DAMAGE_AMOUNT_KEY, event.getAmount()); + effect.setValue(TRIGGERING_CREATURE_KEY, new MageObjectReference(event.getTargetId(), game)); + } + return true; + } + + @Override + public String getRule() { + return "Whenever a creature is dealt damage, {this} deals that much damage to that creature's controller."; + } + + @Override + public TriggeredAbility copy() { + return new RepercussionTriggeredAbility(this); + } +} + +class RepercussionEffect extends OneShotEffect { + + public RepercussionEffect() { + super(Outcome.Damage); + } + + public RepercussionEffect(final RepercussionEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Integer playerDamage = (Integer)this.getValue(RepercussionTriggeredAbility.PLAYER_DAMAGE_AMOUNT_KEY); + MageObjectReference mor = (MageObjectReference)this.getValue(RepercussionTriggeredAbility.TRIGGERING_CREATURE_KEY); + if (playerDamage != null && mor != null) { + Permanent creature = mor.getPermanentOrLKIBattlefield(game); + if (creature != null) { + Player player = game.getPlayer(creature.getControllerId()); + if (player != null) { + player.damage(playerDamage, source.getSourceId(), game, false, true); + } + } + return true; + } + return false; + } + + @Override + public Effect copy() { + return new RepercussionEffect(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/urzaslegacy/ThranLens.java b/Mage.Sets/src/mage/sets/urzaslegacy/ThranLens.java index 254189c4f63..7dfb1ef2d11 100644 --- a/Mage.Sets/src/mage/sets/urzaslegacy/ThranLens.java +++ b/Mage.Sets/src/mage/sets/urzaslegacy/ThranLens.java @@ -74,7 +74,7 @@ public class ThranLens extends CardImpl { @Override public boolean apply(Game game, Ability source) { for (Permanent perm: game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - perm.getColor().setColor(colorless); + perm.getColor(game).setColor(colorless); } return true; } diff --git a/Mage.Sets/src/mage/sets/urzaslegacy/ViashinoHeretic.java b/Mage.Sets/src/mage/sets/urzaslegacy/ViashinoHeretic.java index 4572ec8cfa7..dc603948e84 100644 --- a/Mage.Sets/src/mage/sets/urzaslegacy/ViashinoHeretic.java +++ b/Mage.Sets/src/mage/sets/urzaslegacy/ViashinoHeretic.java @@ -114,5 +114,4 @@ class ViashinoHereticEffect extends OneShotEffect { public String getText(Mode mode) { return "Destroy target artifact. Viashino Heretic deals damage to that artifact's controller equal to the artifact's converted mana cost"; } - } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/urzassaga/Bereavement.java b/Mage.Sets/src/mage/sets/urzassaga/Bereavement.java index 780d76549e3..84c1d1371e4 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Bereavement.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Bereavement.java @@ -84,7 +84,7 @@ class BereavementTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).isDiesEvent()) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); - if (permanent != null && permanent.getCardType().contains(CardType.CREATURE) && permanent.getColor().isGreen()) { + if (permanent != null && permanent.getCardType().contains(CardType.CREATURE) && permanent.getColor(game).isGreen()) { this.getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getControllerId())); return true; } diff --git a/Mage.Sets/src/mage/sets/urzassaga/Persecute.java b/Mage.Sets/src/mage/sets/urzassaga/Persecute.java index dccaf70e7d6..5c83ad2eeaf 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Persecute.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Persecute.java @@ -103,7 +103,7 @@ class PersecuteEffect extends OneShotEffect { controller.revealCards("Persecute", hand, game); Set cards = hand.getCards(game); for (Card card : cards) { - if (card != null && card.getColor().shares(choice.getColor())) { + if (card != null && card.getColor(game).shares(choice.getColor())) { controller.discard(card, source, game); } } diff --git a/Mage.Sets/src/mage/sets/urzassaga/PitTrap.java b/Mage.Sets/src/mage/sets/urzassaga/PitTrap.java index 74821b57853..36b47519922 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/PitTrap.java +++ b/Mage.Sets/src/mage/sets/urzassaga/PitTrap.java @@ -49,7 +49,7 @@ import mage.target.common.TargetAttackingCreature; */ public class PitTrap extends CardImpl { - private static final FilterAttackingCreature filter = new FilterAttackingCreature("creature without flying"); + private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking creature without flying"); static { filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); diff --git a/Mage.Sets/src/mage/sets/urzassaga/Scrap.java b/Mage.Sets/src/mage/sets/urzassaga/Scrap.java index 3d98daa3a90..de35dc13455 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Scrap.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Scrap.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.urzassaga; import java.util.UUID; @@ -35,8 +34,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -45,18 +43,12 @@ import mage.target.TargetPermanent; */ public class Scrap extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Scrap(UUID ownerId) { super(ownerId, 213, "Scrap", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{R}"); this.expansionSetCode = "USG"; this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); } diff --git a/Mage.Sets/src/mage/sets/urzassaga/TitaniasChosen.java b/Mage.Sets/src/mage/sets/urzassaga/TitaniasChosen.java index ccb6947cae5..98192bb6e63 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/TitaniasChosen.java +++ b/Mage.Sets/src/mage/sets/urzassaga/TitaniasChosen.java @@ -91,7 +91,7 @@ class TitaniasChosenAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isGreen()) { + if (spell != null && spell.getColor(game).isGreen()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/vintagemasters/DevoutWitness.java b/Mage.Sets/src/mage/sets/vintagemasters/DevoutWitness.java index 56b9bf4d682..e3b050e15d6 100644 --- a/Mage.Sets/src/mage/sets/vintagemasters/DevoutWitness.java +++ b/Mage.Sets/src/mage/sets/vintagemasters/DevoutWitness.java @@ -39,9 +39,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -50,14 +48,6 @@ import mage.target.TargetPermanent; */ public class DevoutWitness extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public DevoutWitness(UUID ownerId) { super(ownerId, 24, "Devout Witness", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.expansionSetCode = "VMA"; @@ -71,7 +61,7 @@ public class DevoutWitness extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{1}{W}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/vintagemasters/VolrathsShapeshifter.java b/Mage.Sets/src/mage/sets/vintagemasters/VolrathsShapeshifter.java index cd9b949838f..98aeac7b4a3 100644 --- a/Mage.Sets/src/mage/sets/vintagemasters/VolrathsShapeshifter.java +++ b/Mage.Sets/src/mage/sets/vintagemasters/VolrathsShapeshifter.java @@ -105,7 +105,7 @@ class VolrathsShapeshifterEffect extends ContinuousEffectImpl { permanent.getPower().setValue(card.getPower().getValue()); permanent.getToughness().setValue(card.getToughness().getValue()); - permanent.getColor().setColor(card.getColor()); + permanent.getColor(game).setColor(card.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(card.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage.Sets/src/mage/sets/visions/CityOfSolitude.java b/Mage.Sets/src/mage/sets/visions/CityOfSolitude.java index 2bac394e602..2b246421ef1 100644 --- a/Mage.Sets/src/mage/sets/visions/CityOfSolitude.java +++ b/Mage.Sets/src/mage/sets/visions/CityOfSolitude.java @@ -51,7 +51,6 @@ public class CityOfSolitude extends CardImpl { super(ownerId, 52, "City of Solitude", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); this.expansionSetCode = "VIS"; - // Players can cast spells and activate abilities only during their own turns. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CityOfSolitudeEffect())); } @@ -87,11 +86,6 @@ class CityOfSolitudeEffect extends ContinuousRuleModifyingEffectImpl { return !game.getActivePlayerId().equals(event.getPlayerId()); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public CityOfSolitudeEffect copy() { return new CityOfSolitudeEffect(this); diff --git a/Mage.Sets/src/mage/sets/visions/ElephantGrass.java b/Mage.Sets/src/mage/sets/visions/ElephantGrass.java index 11f14fe5eb5..bbbe371753c 100644 --- a/Mage.Sets/src/mage/sets/visions/ElephantGrass.java +++ b/Mage.Sets/src/mage/sets/visions/ElephantGrass.java @@ -90,7 +90,7 @@ class ElephantGrassReplacementEffect extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getTargetId().equals(source.getControllerId()) ) { Permanent creature = game.getPermanent(event.getSourceId()); - if(creature != null && creature.getColor().isBlack()){ + if(creature != null && creature.getColor(game).isBlack()){ return true; } } @@ -145,7 +145,7 @@ class ElephantGrassReplacementEffect2 extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getTargetId().equals(source.getControllerId()) ) { Permanent creature = game.getPermanent(event.getSourceId()); - if (creature != null && !creature.getColor().isBlack()) { + if (creature != null && !creature.getColor(game).isBlack()) { Player attackedPlayer = game.getPlayer(event.getTargetId()); if (attackedPlayer != null) { // only if a player is attacked. Attacking a planeswalker is free diff --git a/Mage.Sets/src/mage/sets/weatherlight/AuraOfSilence.java b/Mage.Sets/src/mage/sets/weatherlight/AuraOfSilence.java index 66627a149a1..a6a93384e85 100644 --- a/Mage.Sets/src/mage/sets/weatherlight/AuraOfSilence.java +++ b/Mage.Sets/src/mage/sets/weatherlight/AuraOfSilence.java @@ -43,9 +43,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.target.TargetPermanent; import mage.util.CardUtil; @@ -55,25 +53,16 @@ import mage.util.CardUtil; * @author emerald000 */ public class AuraOfSilence extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public AuraOfSilence(UUID ownerId) { super(ownerId, 123, "Aura of Silence", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}"); this.expansionSetCode = "WTH"; - // Artifact and enchantment spells your opponents cast cost {2} more to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AuraOfSilenceCostModificationEffect())); // Sacrifice Aura of Silence: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/worldwake/BazaarTrader.java b/Mage.Sets/src/mage/sets/worldwake/BazaarTrader.java index 217f44fa9ad..5eee4cece5c 100644 --- a/Mage.Sets/src/mage/sets/worldwake/BazaarTrader.java +++ b/Mage.Sets/src/mage/sets/worldwake/BazaarTrader.java @@ -36,6 +36,7 @@ import mage.constants.Rarity; import mage.constants.SubLayer; import mage.constants.Zone; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -92,6 +93,8 @@ public class BazaarTrader extends CardImpl { class BazaarTraderEffect extends ContinuousEffectImpl { + MageObjectReference targetPermanentReference; + public BazaarTraderEffect() { super(Duration.Custom, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); this.staticText = "Target player gains control of target artifact, creature, or land you control"; @@ -99,6 +102,7 @@ class BazaarTraderEffect extends ContinuousEffectImpl { public BazaarTraderEffect(final BazaarTraderEffect effect) { super(effect); + this.targetPermanentReference = effect.targetPermanentReference; } @Override @@ -106,12 +110,20 @@ class BazaarTraderEffect extends ContinuousEffectImpl { return new BazaarTraderEffect(this); } + @Override + public void init(Ability source, Game game) { + super.init(source, game); + targetPermanentReference = new MageObjectReference(source.getTargets().get(1).getFirstTarget(), game); + } + @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getFirstTarget()); - Permanent permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); + Permanent permanent = targetPermanentReference.getPermanent(game); if (player != null && permanent != null) { - return permanent.changeControllerId(player.getId(), game); + return permanent.changeControllerId(player.getId(), game); + } else { + discard(); } return false; } diff --git a/Mage.Sets/src/mage/sets/worldwake/Groundswell.java b/Mage.Sets/src/mage/sets/worldwake/Groundswell.java index 1296415b715..9598babd39e 100644 --- a/Mage.Sets/src/mage/sets/worldwake/Groundswell.java +++ b/Mage.Sets/src/mage/sets/worldwake/Groundswell.java @@ -32,17 +32,13 @@ import java.util.UUID; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.SubLayer; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.condition.LockedInCondition; +import mage.abilities.condition.common.LandfallCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.Watcher; import mage.watchers.common.LandfallWatcher; /** @@ -55,11 +51,13 @@ public class Groundswell extends CardImpl { super(ownerId, 104, "Groundswell", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G}"); this.expansionSetCode = "WWK"; - - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new GroundswellEffect(Duration.EndOfTurn)); - + // Target creature gets +2/+2 until end of turn. + //Landfall - If you had a land enter the battlefield under your control this turn, that creature gets +4/+4 until end of turn instead. this.getSpellAbility().addWatcher(new LandfallWatcher()); + this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new BoostTargetEffect(4, 4, Duration.EndOfTurn), new BoostTargetEffect(2, 2, Duration.EndOfTurn), + new LockedInCondition(LandfallCondition.getInstance()), + "Target creature gets +2/+2 until end of turn.
Landfall — If you had a land enter the battlefield under your control this turn, that creature gets +4/44 until end of turn instead")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } public Groundswell(final Groundswell card) { @@ -71,39 +69,3 @@ public class Groundswell extends CardImpl { return new Groundswell(this); } } - -class GroundswellEffect extends ContinuousEffectImpl { - - public GroundswellEffect(Duration duration) { - super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); - staticText = "Target creature gets +2/+2 until end of turn.\nLandfall - If you had a land enter the battlefield under your control this turn, that creature gets +4/+4 until end of turn instead"; - } - - public GroundswellEffect(final GroundswellEffect effect) { - super(effect); - } - - @Override - public GroundswellEffect copy() { - return new GroundswellEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Watcher watcher = game.getState().getWatchers().get("LandPlayed", source.getControllerId()); - Permanent target = (Permanent) game.getPermanent(source.getFirstTarget()); - if (target != null) { - if (watcher != null && watcher.conditionMet()) { - target.addPower(4); - target.addToughness(4); - } - else{ - target.addPower(2); - target.addToughness(2); - } - return true; - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/sets/worldwake/KorFirewalker.java b/Mage.Sets/src/mage/sets/worldwake/KorFirewalker.java index 3e9552fac4a..9e46e536a43 100644 --- a/Mage.Sets/src/mage/sets/worldwake/KorFirewalker.java +++ b/Mage.Sets/src/mage/sets/worldwake/KorFirewalker.java @@ -100,7 +100,7 @@ class KorFirewalkerAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isRed()) { + if (spell != null && spell.getColor(game).isRed()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/MysteriesOfTheDeep.java b/Mage.Sets/src/mage/sets/worldwake/MysteriesOfTheDeep.java index 19a28ff6d7d..804ff917687 100644 --- a/Mage.Sets/src/mage/sets/worldwake/MysteriesOfTheDeep.java +++ b/Mage.Sets/src/mage/sets/worldwake/MysteriesOfTheDeep.java @@ -46,7 +46,6 @@ public class MysteriesOfTheDeep extends CardImpl { super(ownerId, 33, "Mysteries of the Deep", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{4}{U}"); this.expansionSetCode = "WWK"; - // Draw two cards. // Landfall - If you had a land enter the battlefield under your control this turn, draw three cards instead. this.getSpellAbility().addWatcher(new LandfallWatcher()); diff --git a/Mage.Sets/src/mage/sets/worldwake/NaturesClaim.java b/Mage.Sets/src/mage/sets/worldwake/NaturesClaim.java index 08fcea4fdfd..8bfc053fdf2 100644 --- a/Mage.Sets/src/mage/sets/worldwake/NaturesClaim.java +++ b/Mage.Sets/src/mage/sets/worldwake/NaturesClaim.java @@ -25,11 +25,9 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.worldwake; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.Ability; @@ -38,9 +36,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -51,21 +47,15 @@ import mage.target.TargetPermanent; * @author Loki */ public class NaturesClaim extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public NaturesClaim (UUID ownerId) { super(ownerId, 108, "Nature's Claim", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G}"); this.expansionSetCode = "WWK"; + // Destroy target artifact or enchantment. Its controller gains 4 life. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new NaturesClaimEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); } public NaturesClaim (final NaturesClaim card) { @@ -105,5 +95,4 @@ class NaturesClaimEffect extends OneShotEffect { public NaturesClaimEffect copy() { return new NaturesClaimEffect(this); } - } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java b/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java index 27d3d4f059e..0dd3fc178fd 100644 --- a/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java @@ -102,7 +102,7 @@ class PermafrostTrapWatcher extends Watcher { } if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { Permanent perm = game.getPermanent(event.getTargetId()); - if (perm.getCardType().contains(CardType.CREATURE) && perm.getColor().contains(ObjectColor.GREEN) && !perm.getControllerId().equals(controllerId)) { + if (perm.getCardType().contains(CardType.CREATURE) && perm.getColor(game).contains(ObjectColor.GREEN) && !perm.getControllerId().equals(controllerId)) { condition = true; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java b/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java index 8064807857f..de636931bd9 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java @@ -27,6 +27,8 @@ */ package mage.sets.worldwake; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; import mage.constants.CardType; @@ -34,16 +36,19 @@ import mage.constants.Rarity; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.costs.AlternativeCostImpl; -import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.PreventionEffectData; import mage.abilities.effects.PreventionEffectImpl; import mage.cards.CardImpl; -import mage.constants.ColoredManaSymbol; import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.WatcherScope; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.TargetSource; import mage.target.common.TargetCreatureOrPlayer; @@ -66,7 +71,6 @@ public class RefractionTrap extends CardImpl { // Prevent the next 3 damage that a source of your choice would deal to you and/or permanents you control this turn. If damage is prevented this way, Refraction Trap deals that much damage to target creature or player. this.getSpellAbility().addEffect(new RefractionTrapPreventDamageEffect(Duration.EndOfTurn, 3)); - this.getSpellAbility().addTarget(new TargetSource()); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().addWatcher(new RefractionTrapWatcher()); @@ -84,12 +88,15 @@ public class RefractionTrap extends CardImpl { class RefractionTrapWatcher extends Watcher { + Set playersMetCondition = new HashSet<>(); + public RefractionTrapWatcher() { super("RefractionTrapWatcher", WatcherScope.GAME); } public RefractionTrapWatcher(final RefractionTrapWatcher watcher) { super(watcher); + this.playersMetCondition.addAll(watcher.playersMetCondition); } @Override @@ -99,26 +106,33 @@ class RefractionTrapWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (condition == true) //no need to check - condition has already occured - { - return; - } - if (event.getType() == GameEvent.EventType.SPELL_CAST - && game.getOpponents(controllerId).contains(event.getPlayerId())) { + if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell.getColor().isRed()) { + if (spell.getColor(game).isRed()) { if (spell.getCardType().contains(CardType.INSTANT) || spell.getCardType().contains(CardType.SORCERY)) { - condition = true; + playersMetCondition.add(event.getPlayerId()); } } } } + public boolean conditionMetForAnOpponent(UUID controllerId, Game game) { + Player controller = game.getPlayer(controllerId); + if (controller != null) { + for(UUID playerId: playersMetCondition) { + if (controller.hasOpponent(playerId, game)) { + return true; + } + } + } + return false; + + } @Override public void reset() { - super.reset(); - condition = false; + playersMetCondition.clear(); + super.reset(); } } @@ -126,7 +140,7 @@ class RefractionTrapAlternativeCost extends AlternativeCostImpl { public RefractionTrapAlternativeCost() { super("You may pay {W} rather than pay Refraction Trap's mana cost"); - this.add(new ColoredManaCost(ColoredManaSymbol.W)); + this.add(new ManaCostsImpl("{W}")); } public RefractionTrapAlternativeCost(final RefractionTrapAlternativeCost cost) { @@ -141,10 +155,7 @@ class RefractionTrapAlternativeCost extends AlternativeCostImpl { @Override public boolean isAvailable(Game game, Ability source) { RefractionTrapWatcher watcher = (RefractionTrapWatcher) game.getState().getWatchers().get("RefractionTrapWatcher"); - if (watcher != null && watcher.conditionMet()) { - return true; - } - return false; + return watcher != null && watcher.conditionMetForAnOpponent(source.getControllerId(), game); } @Override @@ -155,24 +166,33 @@ class RefractionTrapAlternativeCost extends AlternativeCostImpl { class RefractionTrapPreventDamageEffect extends PreventionEffectImpl { + private final TargetSource target; private int amount; public RefractionTrapPreventDamageEffect(Duration duration, int amount) { - super(duration); + super(duration, amount, false, false); this.amount = amount; + this.target = new TargetSource(); staticText = "The next " + amount + " damage that a source of your choice would deal to you and/or permanents you control this turn. If damage is prevented this way, {this} deals that much damage to target creature or player"; } public RefractionTrapPreventDamageEffect(final RefractionTrapPreventDamageEffect effect) { super(effect); this.amount = effect.amount; + this.target = effect.target.copy(); } @Override public RefractionTrapPreventDamageEffect copy() { return new RefractionTrapPreventDamageEffect(this); } - + + @Override + public void init(Ability source, Game game) { + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); + super.init(source, game); + } + @Override public boolean apply(Game game, Ability source) { return true; @@ -180,45 +200,29 @@ class RefractionTrapPreventDamageEffect extends PreventionEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - GameEvent preventEvent = new GameEvent(GameEvent.EventType.PREVENT_DAMAGE, source.getFirstTarget(), source.getSourceId(), source.getControllerId(), event.getAmount(), false); - if (!game.replaceEvent(preventEvent)) { - int prevented = 0; - if (event.getAmount() >= this.amount) { - int damage = amount; - event.setAmount(event.getAmount() - amount); - this.used = true; - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, source.getFirstTarget(), source.getSourceId(), source.getControllerId(), damage)); - prevented = damage; - } else { - int damage = event.getAmount(); - event.setAmount(0); - amount -= damage; - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, source.getFirstTarget(), source.getSourceId(), source.getControllerId(), damage)); - prevented = damage; + PreventionEffectData preventionData = preventDamageAction(event, source, game); + this.used = true; + this.discard(); // only one use + if (preventionData.getPreventedDamage() > 0) { + UUID damageTarget = getTargetPointer().getFirst(game, source); + Permanent permanent = game.getPermanent(damageTarget); + if (permanent != null) { + game.informPlayers("Dealing " + preventionData.getPreventedDamage() + " to " + permanent.getLogName()); + permanent.damage(preventionData.getPreventedDamage(), source.getSourceId(), game, false, true); } - - // deal damage now - if (prevented > 0) { - UUID damageTarget = source.getTargets().get(1).getFirstTarget(); - Permanent target = game.getPermanent(damageTarget); - if (target != null) { - game.informPlayers("Dealing " + prevented + " to " + target.getName()); - target.damage(prevented, source.getSourceId(), game, false, true); - } - Player player = game.getPlayer(damageTarget); - if (player != null) { - game.informPlayers("Dealing " + prevented + " to " + player.getLogName()); - player.damage(prevented, source.getSourceId(), game, true, false); - } + Player player = game.getPlayer(damageTarget); + if (player != null) { + game.informPlayers("Dealing " + preventionData.getPreventedDamage() + " to " + player.getLogName()); + player.damage(preventionData.getPreventedDamage(), source.getSourceId(), game, true, false); } } + return false; } @Override public boolean applies(GameEvent event, Ability source, Game game) { if (!this.used && super.applies(event, source, game)) { - // check source MageObject object = game.getObject(event.getSourceId()); if (object == null) { @@ -226,7 +230,9 @@ class RefractionTrapPreventDamageEffect extends PreventionEffectImpl { return false; } - if (!object.getId().equals(source.getFirstTarget())) { + // check damage source + if (!object.getId().equals(target.getFirstTarget()) && + !((object instanceof StackObject) && ((StackObject)object).getSourceId().equals(target.getFirstTarget()))) { return false; } diff --git a/Mage.Sets/src/mage/sets/worldwake/RestForTheWeary.java b/Mage.Sets/src/mage/sets/worldwake/RestForTheWeary.java index 890df85a5a4..08a543c1340 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RestForTheWeary.java +++ b/Mage.Sets/src/mage/sets/worldwake/RestForTheWeary.java @@ -47,7 +47,6 @@ public class RestForTheWeary extends CardImpl { super(ownerId, 18, "Rest for the Weary", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{W}"); this.expansionSetCode = "WWK"; - // Target player gains 4 life. // Landfall - If you had a land enter the battlefield under your control this turn, that player gains 8 life instead. this.getSpellAbility().addWatcher(new LandfallWatcher()); diff --git a/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java b/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java index 573cb143468..f831a87dc80 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java @@ -110,7 +110,7 @@ class RicochetTrapWatcher extends Watcher { if (event.getType() == EventType.SPELL_CAST && game.getOpponents(controllerId).contains(event.getPlayerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell.getColor().isBlue()) { + if (spell.getColor(game).isBlue()) { condition = true; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java b/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java index ec39deffdac..c5b2b73ac36 100644 --- a/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java +++ b/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java @@ -59,7 +59,6 @@ public class SearingBlaze extends CardImpl { super(ownerId, 90, "Searing Blaze", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}{R}"); this.expansionSetCode = "WWK"; - // Searing Blaze deals 1 damage to target player and 1 damage to target creature that player controls. // Landfall - If you had a land enter the battlefield under your control this turn, Searing Blaze deals 3 damage to that player and 3 damage to that creature instead. this.getSpellAbility().addTarget(new TargetPlayer()); diff --git a/Mage.Sets/src/mage/sets/worldwake/SlingbowTrap.java b/Mage.Sets/src/mage/sets/worldwake/SlingbowTrap.java index c91558c586a..6d3fa2bceeb 100644 --- a/Mage.Sets/src/mage/sets/worldwake/SlingbowTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/SlingbowTrap.java @@ -101,7 +101,7 @@ class SlingbowTrapAlternativeCost extends AlternativeCostImpl { List attackers = game.getCombat().getAttackers(); for (UUID creatureId : attackers) { Permanent creature = game.getPermanent(creatureId); - if (creature.getColor().isBlack() + if (creature.getColor(game).isBlack() && creature.getAbilities().contains(FlyingAbility.getInstance())) { return true; } diff --git a/Mage.Sets/src/mage/sets/worldwake/TombHex.java b/Mage.Sets/src/mage/sets/worldwake/TombHex.java index 1204445582d..6a42e79ddcf 100644 --- a/Mage.Sets/src/mage/sets/worldwake/TombHex.java +++ b/Mage.Sets/src/mage/sets/worldwake/TombHex.java @@ -50,13 +50,12 @@ public class TombHex extends CardImpl { super(ownerId, 69, "Tomb Hex", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{B}"); this.expansionSetCode = "WWK"; - // Target creature gets -2/-2 until end of turn. // Landfall - If you had a land enter the battlefield under your control this turn, that creature gets -4/-4 until end of turn instead. this.getSpellAbility().addWatcher(new LandfallWatcher()); this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new BoostTargetEffect(-4, -4, Duration.EndOfTurn), new BoostTargetEffect(-2, -2, Duration.EndOfTurn), new LockedInCondition(LandfallCondition.getInstance()), - "Target creature gets -2/-2 until end of turn. Landfall - If you had a land enter the battlefield under your control this turn, that creature gets -4/-4 until end of turn instead")); + "Target creature gets -2/-2 until end of turn.
Landfall — If you had a land enter the battlefield under your control this turn, that creature gets -4/-4 until end of turn instead")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/sets/zendikar/BloodchiefAscension.java b/Mage.Sets/src/mage/sets/zendikar/BloodchiefAscension.java index d57c523f802..de0290a429d 100644 --- a/Mage.Sets/src/mage/sets/zendikar/BloodchiefAscension.java +++ b/Mage.Sets/src/mage/sets/zendikar/BloodchiefAscension.java @@ -57,10 +57,9 @@ public class BloodchiefAscension extends CardImpl { super(ownerId, 82, "Bloodchief Ascension", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{B}"); this.expansionSetCode = "ZEN"; - // At the beginning of each end step, if an opponent lost 2 or more life this turn, you may put a quest counter on Bloodchief Ascension. (Damage causes loss of life.) this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, - new AddCountersSourceEffect(CounterType.QUEST.createInstance(), false), + new AddCountersSourceEffect(CounterType.QUEST.createInstance(1), false), TargetController.ANY, new OpponentLostLifeCondition(Condition.ComparisonType.GreaterThan, 1), true)); diff --git a/Mage.Sets/src/mage/sets/zendikar/ChandraAblaze.java b/Mage.Sets/src/mage/sets/zendikar/ChandraAblaze.java index fb8c03d134b..e7451fd3a29 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ChandraAblaze.java +++ b/Mage.Sets/src/mage/sets/zendikar/ChandraAblaze.java @@ -145,7 +145,7 @@ class ChandraAblazeEffect2 extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Card card = (Card) this.getValue("discardedCard"); - if (card != null && card.getColor().isRed()) { + if (card != null && card.getColor(game).isRed()) { Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); if (permanent != null) { permanent.damage(4, source.getSourceId(), game, false, true); diff --git a/Mage.Sets/src/mage/sets/zendikar/IonaShieldOfEmeria.java b/Mage.Sets/src/mage/sets/zendikar/IonaShieldOfEmeria.java index 8bfef2890eb..6bf1a7ce352 100644 --- a/Mage.Sets/src/mage/sets/zendikar/IonaShieldOfEmeria.java +++ b/Mage.Sets/src/mage/sets/zendikar/IonaShieldOfEmeria.java @@ -151,7 +151,7 @@ class IonaShieldOfEmeriaReplacementEffect extends ContinuousRuleModifyingEffectI if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId()) ) { ObjectColor chosenColor = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color"); Card card = game.getCard(event.getSourceId()); - if (chosenColor != null && card != null && card.getColor().contains(chosenColor)) { + if (chosenColor != null && card != null && card.getColor(game).contains(chosenColor)) { return true; } } diff --git a/Mage.Sets/src/mage/sets/zendikar/KorSanctifiers.java b/Mage.Sets/src/mage/sets/zendikar/KorSanctifiers.java index 938a174c885..ef0d2c637cd 100644 --- a/Mage.Sets/src/mage/sets/zendikar/KorSanctifiers.java +++ b/Mage.Sets/src/mage/sets/zendikar/KorSanctifiers.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.zendikar; import java.util.UUID; @@ -38,9 +37,7 @@ import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -48,13 +45,6 @@ import mage.target.TargetPermanent; * @author Loki */ public class KorSanctifiers extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public KorSanctifiers (UUID ownerId) { super(ownerId, 22, "Kor Sanctifiers", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{W}"); @@ -68,10 +58,9 @@ public class KorSanctifiers extends CardImpl { // Kicker {W} (You may pay an additional {W} as you cast this spell.) this.addAbility(new KickerAbility("{W}")); - // When Kor Sanctifiers enters the battlefield, if it was kicked, destroy target artifact or enchantment. EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(new ConditionalTriggeredAbility(ability, KickedCondition.getInstance(), "When {this} enters the battlefield, if it was kicked, destroy target artifact or enchantment.")); } @@ -83,5 +72,4 @@ public class KorSanctifiers extends CardImpl { public KorSanctifiers copy() { return new KorSanctifiers(this); } - } diff --git a/Mage.Sets/src/mage/sets/zendikar/RelicCrush.java b/Mage.Sets/src/mage/sets/zendikar/RelicCrush.java index 57f4342258e..150a3c4c73d 100644 --- a/Mage.Sets/src/mage/sets/zendikar/RelicCrush.java +++ b/Mage.Sets/src/mage/sets/zendikar/RelicCrush.java @@ -35,8 +35,7 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -47,24 +46,15 @@ import mage.target.TargetPermanent; */ public class RelicCrush extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - (new CardTypePredicate(CardType.ARTIFACT)), - (new CardTypePredicate(CardType.ENCHANTMENT)))); - } - public RelicCrush(UUID ownerId) { super(ownerId, 179, "Relic Crush", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{4}{G}"); this.expansionSetCode = "ZEN"; - // Destroy target artifact or enchantment and up to one other target artifact or enchantment. + FilterPermanent filter = new FilterArtifactOrEnchantmentPermanent(); this.getSpellAbility().addEffect(new RelicCrushEffect()); this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter, false)); - } public RelicCrush(final RelicCrush card) { diff --git a/Mage.Sets/src/mage/sets/zendikar/SurrakarMarauder.java b/Mage.Sets/src/mage/sets/zendikar/SurrakarMarauder.java index 0e317d3a684..1f10bd4ea43 100644 --- a/Mage.Sets/src/mage/sets/zendikar/SurrakarMarauder.java +++ b/Mage.Sets/src/mage/sets/zendikar/SurrakarMarauder.java @@ -52,6 +52,8 @@ public class SurrakarMarauder extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); + // Landfall - Whenever a land enters the battlefield under your control, Surrakar Marauder gains intimidate until end of turn. + // (It can't be blocked except by artifact creatures and/or creatures that share a color with it.) this.addAbility(new LandfallAbility(new GainAbilitySourceEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn), false)); } diff --git a/Mage.Tests/CommanderDuel.dck b/Mage.Tests/CommanderDuel.dck new file mode 100644 index 00000000000..2999d2ef2b5 --- /dev/null +++ b/Mage.Tests/CommanderDuel.dck @@ -0,0 +1,73 @@ +NAME:Sworn to Darkness +1 [C14:54] Commander's Sphere +1 [C14:307] Polluted Mire +1 [C14:59] Arcane Lighthouse +1 [C14:58] Unstable Obelisk +1 [C14:149] Morkrut Banshee +1 [C14:148] Magus of the Coffers +1 [C14:147] Liliana's Reaver +1 [C14:146] Gray Merchant of Asphodel +1 [C14:145] Grave Titan +1 [C14:144] Evernight Shade +1 [C14:143] Dregs of Sorrow +1 [C14:142] Dread Return +1 [C14:141] Drana, Kalastria Bloodchief +1 [C14:140] Disciple of Bolas +1 [C14:23] Ghoulcaller Gisa +1 [C14:22] Flesh Carver +1 [C14:21] Demon of Wailing Agonies +1 [C14:61] Myriad Landscape +1 [C14:29] Raving Dead +1 [C14:28] Overseer of the Damned +1 [C14:26] Necromantic Selection +1 [C14:25] Malicious Affliction +1 [C14:24] Infernal Offering +1 [C14:159] Reaper from the Abyss +1 [C14:235] Charcoal Diamond +1 [C14:158] Read the Bones +1 [C14:157] Promise of Power +1 [C14:156] Profane Command +1 [C14:232] Burnished Hart +1 [C14:155] Pontiff of Blight +1 [C14:154] Phyrexian Gargantua +1 [C14:275] Swiftfoot Boots +1 [C14:153] Pestilence Demon +1 [C14:152] Nekrataal +1 [C14:151] Nantuko Shade +1 [C14:150] Mutilate +1 [C14:270] Sol Ring +1 [C14:31] Wake the Dead +1 [C14:30] Spoils of Blood +8 [C14:329] Swamp +8 [C14:328] Swamp +8 [C14:327] Swamp +8 [C14:326] Swamp +1 [C14:169] Victimize +1 [C14:168] Vampire Hexmage +1 [C14:245] Lashwrithe +1 [C14:167] Tragic Slip +1 [C14:288] Crypt of Agadeem +1 [C14:243] Jet Medallion +1 [C14:166] Tendrils of Corruption +1 [C14:165] Syphon Mind +1 [C14:164] Sudden Spoiling +1 [C14:285] Bojuka Bog +1 [C14:163] Skirsdag High Priest +1 [C14:284] Barren Moor +1 [C14:162] Skeletal Scrying +1 [C14:282] Worn Powerstone +1 [C14:161] Sign in Blood +1 [C14:160] Shriekmaw +1 [C14:139] Crypt Ghast +1 [C14:138] Butcher of Malakir +1 [C14:137] Bloodgift Demon +1 [C14:136] Black Sun's Zenith +1 [C14:135] Bad Moon +1 [C14:134] Annihilate +1 [C14:133] AEther Snap +1 [C14:298] Ghost Quarter +1 [C14:132] Abyssal Persecutor +1 [C14:250] Mind Stone +1 [C14:294] Everglades +1 [C14:170] Xathrid Demon +SB: 1 [C14:27] Ob Nixilis of the Black Oath diff --git a/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastCreaturesTest.java b/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastCreaturesTest.java new file mode 100644 index 00000000000..f65e7655177 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastCreaturesTest.java @@ -0,0 +1,84 @@ +/* + * 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 org.mage.test.AI.basic; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseAI; + +/** + * + * @author LevelX2 + */ +public class CastCreaturesTest extends CardTestPlayerBaseAI { + + /** + * Tests that the creature is cast if enough mana is available + */ + @Test + public void testSimpleCast() { + addCard(Zone.HAND, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + } + + @Test + public void testSimpleCast2() { + addCard(Zone.HAND, playerA, "Silvercoat Lion"); + addCard(Zone.HAND, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Silvercoat Lion", 2); + } + + @Test + @Ignore // AI should cast Myr Enforcer -> Check why it does not + public void testSimpleCast3() { + // Affinity for artifacts (This spell costs less to cast for each artifact you control.) + addCard(Zone.HAND, playerA, "Myr Enforcer"); + // {T}: Add to your mana pool. + // {T}, {1}, Sacrifice Mind Stone: Draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Mind Stone", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Myr Enforcer", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/add/AddAbilitiesToNonPermanentsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/add/AddAbilitiesToNonPermanentsTest.java new file mode 100644 index 00000000000..242f9e5c2a2 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/add/AddAbilitiesToNonPermanentsTest.java @@ -0,0 +1,78 @@ +/* + * 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 org.mage.test.cards.abilities.add; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class AddAbilitiesToNonPermanentsTest extends CardTestPlayerBase { + + + /** + * With Teferi, Mage of Zhalfir on the battlefield it has to be possible to search for + * a God of Storms in the deck by using Mystical Teachings. + * + */ + @Test + public void testSearchForCardWithFlashInLibrary() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + // Flash + // Creature cards you own that aren't on the battlefield have flash. + // Each opponent can cast spells only any time he or she could cast a sorcery. + addCard(Zone.BATTLEFIELD, playerA, "Teferi, Mage of Zhalfir"); + + // Search your library for an instant card or a card with flash, reveal it, and put it into your hand. Then shuffle your library. + // Flashback {5}{B} + addCard(Zone.HAND, playerA, "Mystical Teachings"); // "{3}{U}" + + addCard(Zone.LIBRARY, playerA, "Keranos, God of Storms"); + addCard(Zone.LIBRARY, playerA, "Plains", 3); + + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mystical Teachings"); + addTarget(playerA, "Keranos, God of Storms"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Mystical Teachings", 1); + assertHandCount(playerA, "Keranos, God of Storms", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java index bf2e2bfe77e..48e4bbd1d9b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java @@ -232,6 +232,63 @@ public class CursesTest extends CardTestPlayerBase { assertLife(playerA, 20); assertLife(playerB, 20); assertPermanentCount(playerA, "Curse of Misfortunes", 1); - assertPermanentCount(playerA, "Curse of Bloodletting", 1); } + assertPermanentCount(playerA, "Curse of Bloodletting", 1); + } + + + @Test + public void testCurseOfDeathsHold() { + // Creatures enchanted player controls get -1/-1. + addCard(Zone.HAND, playerA, "Curse of Death's Hold"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB); + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPermanentCount(playerA, "Curse of Death's Hold", 1); + + assertPowerToughness(playerB, "Silvercoat Lion", 1, 1); + } + + @Test + public void testCurseOfDeathsHold2() { + // Creatures enchanted player controls get -1/-1. + addCard(Zone.HAND, playerA, "Curse of Death's Hold"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, "Tasigur, the Golden Fang", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 3); + addCard(Zone.HAND, playerB, "Reclamation Sage"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Reclamation Sage"); + addTarget(playerB, "Curse of Death's Hold"); + + // {2}{G/U}{G/U}: Put the top two cards of your library into your graveyard, then return a nonland card of an opponent's choice from your graveyard to your hand. + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{G/U}{G/U}: Put the top two cards"); + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB); + + setStopAt(3, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerB, "Reclamation Sage", 1); + assertPermanentCount(playerA, "Curse of Death's Hold", 1); + assertGraveyardCount(playerA, 2); + + assertPowerToughness(playerB, "Silvercoat Lion", 1, 1); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CycleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CyclingTest.java similarity index 82% rename from Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CycleTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CyclingTest.java index a0f1f8e8dd5..e50598c40b4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CycleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CyclingTest.java @@ -37,7 +37,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author LevelX2 */ -public class CycleTest extends CardTestPlayerBase { +public class CyclingTest extends CardTestPlayerBase { /** * 702.28. Cycling @@ -60,7 +60,7 @@ public class CycleTest extends CardTestPlayerBase { */ @Test - public void CycleAndTriggerTest() { + public void cycleAndTriggerTest() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); // Destroy all creatures. They can't be regenerated. Draw a card for each creature destroyed this way. // Cycling {3}{B}{B} @@ -82,15 +82,13 @@ public class CycleTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Pillarfield Ox", 1); assertPowerToughness(playerB, "Pillarfield Ox", 0, 2); - - } /** * Cycle from graveyard or battlefield may not work */ @Test - public void CycleFromGraveyard() { + public void cycleFromGraveyard() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); // Destroy all creatures. They can't be regenerated. Draw a card for each creature destroyed this way. // Cycling {3}{B}{B} @@ -113,5 +111,32 @@ public class CycleTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Disciple Of Grace", 1); } + + /** + * Type cycling for sliver + */ + @Test + public void cycleFromHomingSliver() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + // Each Sliver card in each player's hand has slivercycling {3}. + addCard(Zone.BATTLEFIELD, playerA, "Homing Sliver"); + // All Sliver creatures have flying. + addCard(Zone.HAND, playerA, "Winged Sliver"); + + addCard(Zone.LIBRARY, playerA, "Horned Sliver"); + addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 10); + skipInitShuffling(); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Slivercycling {3}"); + addTarget(playerA, "Horned Sliver"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertHandCount(playerA, 1); + + assertGraveyardCount(playerA, "Winged Sliver", 1); + + assertHandCount(playerA, "Horned Sliver", 1); // searched by slivercyclibng + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FadingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FadingTest.java index 77cee8a7741..078942e082b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FadingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FadingTest.java @@ -91,7 +91,7 @@ public class FadingTest extends CardTestPlayerBase { public void testFadesAway() { addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); addCard(Zone.HAND, playerA, "Blastoderm"); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blastoderm"); setStopAt(9, PhaseStep.BEGIN_COMBAT); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java new file mode 100644 index 00000000000..e3786359193 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java @@ -0,0 +1,78 @@ +/* + * 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 org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class FlashbackTest extends CardTestPlayerBase { + + /** + * Fracturing Gust is bugged. In a match against Affinity, it worked + * properly when cast from hand. When I cast it from graveyard c/o + * Snapcaster Mage flashback, it destroyed my opponent's Darksteel Citadels, + * which it did not do when cast from my hand. + */ + @Test + public void testSnapcasterMageWithFracturingGust() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + addCard(Zone.HAND, playerA, "Snapcaster Mage", 1); + + // Destroy all artifacts and enchantments. You gain 2 life for each permanent destroyed this way. + addCard(Zone.GRAVEYARD, playerA, "Fracturing Gust"); + + addCard(Zone.BATTLEFIELD, playerA, "Berserkers' Onslaught", 1); + addCard(Zone.BATTLEFIELD, playerB, "Darksteel Citadel", 1); + + + // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage"); + setChoice(playerA, "Fracturing Gust"); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback {2}{G/W}{G/W}{G/W}"); // now snapcaster mage is died so -13/-13 + + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Snapcaster Mage", 1); + assertGraveyardCount(playerA, "Berserkers' Onslaught", 1); + + assertPermanentCount(playerB, "Darksteel Citadel", 1); + + assertExileCount("Fracturing Gust", 1); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HeroicTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HeroicTest.java new file mode 100644 index 00000000000..9ba14bc6326 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HeroicTest.java @@ -0,0 +1,79 @@ +/* + * 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 org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class HeroicTest extends CardTestPlayerBase { + + /** + * When casting Dromoka's Command targeting two of my own Heroic creatures, only one of them triggers. + * It appears to be the one targeted with mode 4 (fight) rather than the one targeted with mode 3 (+1/+1 counter). + * Screenshot attached. Reproducible. + */ + + @Test + public void testHeroicWithModal() { + // Heroic - Whenever you cast a spell that targets Favored Hoplite, put a +1/+1 counter on Favored Hoplite and prevent all damage that would be dealt to it this turn. + addCard(Zone.BATTLEFIELD, playerA, "Favored Hoplite", 1); // 1/2 + // Heroic — Whenever you cast a spell that targets Lagonna-Band Trailblazer, put a +1/+1 counter on Lagonna-Band Trailblazer. + addCard(Zone.BATTLEFIELD, playerA, "Lagonna-Band Trailblazer"); // 0/4 + + // Mode 3 = Put a +1/+1 counter on target creature + // Mode 4 = Target creature you control fights target creature you don't control + addCard(Zone.HAND, playerA, "Dromoka's Command", 1); // {G}{W} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dromoka's Command", "mode=3Lagonna-Band Trailblazer^mode=4Favored Hoplite^Silvercoat Lion"); + // Silvercoat lion will be set by AI as only possible target + setModeChoice(playerA, "3"); + setModeChoice(playerA, "4"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Dromoka's Command", 1); + + assertPowerToughness(playerA, "Favored Hoplite", 2, 3); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Lagonna-Band Trailblazer", 2, 6); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java index 10d0b3c74e2..457f3407508 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java @@ -76,7 +76,9 @@ public class HexproofTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Island", 4); addCard(Zone.HAND, playerB, "Into the Void"); + // Return up to two target creatures to their owners' hands. castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Into the Void", "Elder of Laurels^Arbor Elf"); + // Target creature you control gets +1/+1 and gains hexproof until end of turn. (It can't be the target of spells or abilities your opponents control.) castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Ranger's Guile", "Elder of Laurels"); setStopAt(2, PhaseStep.END_TURN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java new file mode 100644 index 00000000000..ef5349b9ae8 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java @@ -0,0 +1,115 @@ +/* + * 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 org.mage.test.cards.abilities.keywords; + +import mage.abilities.keyword.IntimidateAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class LandfallTest extends CardTestPlayerBase { + + @Test + public void testNormalUse() { + addCard(Zone.BATTLEFIELD, playerA, "Plains",3); + addCard(Zone.HAND, playerA, "Plains"); + + // Instant - {1}{W} + // Target player gains 4 life. + // Landfall - If you had a land enter the battlefield under your control this turn, that player gains 8 life instead. + addCard(Zone.HAND, playerA, "Rest for the Weary",2); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plains"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest for the Weary"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest for the Weary"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Plains", 4); + assertGraveyardCount(playerA, "Rest for the Weary", 2); + assertLife(playerA, 32); // + 8 from 1 turn + 4 from second turn + assertLife(playerB, 20); + + } + /** + * If you Hive Mind an opponent's Rest for the Weary and redirect its target to yourself when it's not your turn, + * the game spits out this message and rolls back to before Rest for the Weary was cast. + * + */ + @Test + public void testHiveMind() { + addCard(Zone.BATTLEFIELD, playerA, "Plains",2); + + // Whenever a player casts an instant or sorcery spell, each other player copies that spell. Each of those players may choose new targets for his or her copy. + addCard(Zone.BATTLEFIELD, playerB, "Hive Mind"); + + // Instant - {1}{W} + // Target player gains 4 life. + // Landfall - If you had a land enter the battlefield under your control this turn, that player gains 8 life instead. + addCard(Zone.HAND, playerA, "Rest for the Weary",1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest for the Weary"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Rest for the Weary", 1); + assertLife(playerA, 24); + assertLife(playerB, 24); + + } + + @Test + public void testSurrakarMarauder() { + // Landfall - Whenever a land enters the battlefield under your control, Surrakar Marauder gains intimidate until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Surrakar Marauder",1); + addCard(Zone.HAND, playerA, "Plains"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plains"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Plains", 1); + + assertAbility(playerA, "Surrakar Marauder", IntimidateAbility.getInstance(), true); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java index 775d820ec24..f980b8be196 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java @@ -362,4 +362,40 @@ public class ManifestTest extends CardTestPlayerBase { assertPermanentCount(playerB, "", 1); } + + /** + * Whisperwood Elemental - Its sacrifice ability doesn't work.. + * + */ + @Test + public void testWhisperwoodElemental() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Seismic Rupture deals 2 damage to each creature without flying. + addCard(Zone.HAND, playerA, "Seismic Rupture", 1); + + // At the beginning of your end step, manifest the top card of your library. + // Sacrifice Whisperwood Elemental: Until end of turn, face-up, nontoken creatures you control gain "When this creature dies, manifest the top card of your library." + addCard(Zone.BATTLEFIELD, playerB, "Whisperwood Elemental", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 2); + + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Sacrifice"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Seismic Rupture"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Seismic Rupture", 1); + assertGraveyardCount(playerB, "Whisperwood Elemental", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 2); + + assertPermanentCount(playerB, "", 2); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java new file mode 100644 index 00000000000..42a390fefcf --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java @@ -0,0 +1,69 @@ +/* + * 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 org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class MetalcraftTest extends CardTestPlayerBase { + + /** + * Rusted Relic or Blinkmoth nexus is bugged + * Either Relic does not see Blinkmoth as an artifact or it does not turn + * into one when it should. + * + */ + @Test + public void testMetalcraftFromBlinkmoth() { + addCard(Zone.BATTLEFIELD, playerA, "Darksteel Citadel",1); + + // Metalcraft - {this} is a 5/5 Golem artifact creature as long as you control three or more artifacts + addCard(Zone.BATTLEFIELD, playerA, "Rusted Relic", 1); + + // {T}: Add {1}to your mana pool. + // {1}: Blinkmoth Nexus becomes a 1/1 Blinkmoth artifact creature with flying until end of turn. It's still a land. + // {1}, {T}: Target Blinkmoth creature gets +1/+1 until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Blinkmoth Nexus", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: Until end of turn {this} becomes "); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Blinkmoth Nexus", 1, 1); + assertPowerToughness(playerA, "Rusted Relic", 5, 5); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SatyrFiredancerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SatyrFiredancerTest.java index b97cd37240f..e98e58bc6be 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SatyrFiredancerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SatyrFiredancerTest.java @@ -29,7 +29,6 @@ package org.mage.test.cards.abilities.oneshot.damage; import mage.constants.PhaseStep; import mage.constants.Zone; -import mage.counters.CounterType; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -83,4 +82,23 @@ public class SatyrFiredancerTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Silvercoat Lion", 1); } + + @Test + public void testDamageFromOtherCreature() { + // Whenever an instant or sorcery spell you control deals damage to an opponent, Satyr Firedancer deals that much damage to target creature that player controls. + addCard(Zone.BATTLEFIELD, playerA, "Satyr Firedancer"); + + // {T}: Prodigal Pyromancer deals 1 damage to target creature or player. + addCard(Zone.BATTLEFIELD, playerA, "Prodigal Pyromancer", 1); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", playerB); + addTarget(playerA, playerB); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 19); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/MycosynthGolemTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/MycosynthGolemTest.java index 69cefe72618..0de986e4214 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/MycosynthGolemTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/MycosynthGolemTest.java @@ -30,7 +30,8 @@ package org.mage.test.cards.abilities.other; import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; +import mage.game.permanent.Permanent; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -50,10 +51,11 @@ public class MycosynthGolemTest extends CardTestPlayerBase { * */ - @Ignore // at this time player.getPlayable() does not account for spells that gain abilities + // @Ignore // at this time player.getPlayable() does not account for spells that gain abilities @Test public void testSpellsAffinity() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); addCard(Zone.BATTLEFIELD, playerA, "Mycosynth Golem"); addCard(Zone.HAND, playerA, "Alpha Myr"); @@ -64,6 +66,17 @@ public class MycosynthGolemTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Alpha Myr", 1); assertHandCount(playerA, "Alpha Myr", 0); + + Permanent mountain = getPermanent("Mountain", playerA); + Permanent forest = getPermanent("Forest", playerA); + int tappedLands = 0; + if (mountain.isTapped()) { + tappedLands++; + } + if (forest.isTapped()) { + tappedLands++; + } + Assert.assertEquals("only one land may be tapped because the cost reduction", 1, tappedLands); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NecromancyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NecromancyTest.java index 54b959bb00b..5b2a9ae2f6c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NecromancyTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NecromancyTest.java @@ -112,8 +112,8 @@ public class NecromancyTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Disenchant"); addCard(Zone.GRAVEYARD, playerA, "Craw Wurm"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromancy"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Disenchant", "Necromancy"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromancy"); // enchanting the Craw Wurm + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Disenchant", "Necromancy"); // if Necromancy leaves, the enchanted creature has to leave too setStopAt(1, PhaseStep.END_TURN); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java index a55b021a775..0c2a6912486 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java @@ -79,4 +79,49 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Worldheart Phoenix", 1); } + + + @Test + public void testNarsetEnlightenedMaster() { + // First strike + // Hexproof + // Whenever Narset, Enlightented Master attacks, exile the top four cards of your library. Until end of turn, you may cast noncreature cards exiled with Narset this turn without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerB, "Narset, Enlightened Master", 1); + + skipInitShuffling(); + addCard(Zone.LIBRARY, playerB, "Silvercoat Lion"); + addCard(Zone.LIBRARY, playerB, "Abzan Banner"); + // Ferocious - If you control a creature with power 4 or greater, you may cast Dragon Grip as though it had flash. (You may cast it any time you could cast an instant.) + // Enchant creature + // Enchanted creature gets +2/+0 and has first strike. + addCard(Zone.LIBRARY, playerB, "Dragon Grip"); + // You gain 2 life for each creature you control. + addCard(Zone.LIBRARY, playerB, "Peach Garden Oath"); + addCard(Zone.LIBRARY, playerB, "Plains"); + + attack(2, playerB, "Narset, Enlightened Master"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Silvercoat Lion"); // can't be cast from exile + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Abzan Banner"); // can be cast from exile + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Dragon Grip", "Narset, Enlightened Master"); // can be cast from exile + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Peach Garden Oath"); // can be cast from exile + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertExileCount("Silvercoat Lion", 1); + assertPermanentCount(playerB, "Abzan Banner", 1); + assertPermanentCount(playerB, "Dragon Grip", 1); + assertGraveyardCount(playerB, "Peach Garden Oath", 1); + + assertPowerToughness(playerB, "Narset, Enlightened Master", 5, 2); + + assertHandCount(playerB, "Plains", 1); + assertLife(playerA, 17); + assertLife(playerB, 22); + + + } + + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MightOfOldKrosaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MightOfOldKrosaTest.java new file mode 100644 index 00000000000..17db1377bd5 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MightOfOldKrosaTest.java @@ -0,0 +1,83 @@ +/* + * 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 org.mage.test.cards.continuous; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class MightOfOldKrosaTest extends CardTestPlayerBase { + + @Test + public void testTwiceMightOfOldKrosaBeginCombat() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.HAND, playerA, "Might of Old Krosa", 2); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Might of Old Krosa", "Silvercoat Lion"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Might of Old Krosa", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Might of Old Krosa", 2); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 6, 6); + } + /** + * Threw two Might of old Krosa's onto a creature, but only one had any effect. + */ + + @Test + public void testTwiceMightOfOldKrosa() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.HAND, playerA, "Might of Old Krosa", 2); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Might of Old Krosa", "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Might of Old Krosa", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Might of Old Krosa", 2); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 10, 10); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java new file mode 100644 index 00000000000..2a45bf0f4a6 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java @@ -0,0 +1,250 @@ +/* + * 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 org.mage.test.cards.continuous; + +import mage.cards.Card; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class PaintersServantTest extends CardTestPlayerBase { + + /** + * Test that the added color is applied as Painter's Servant is on the battlefield + */ + @Test + public void testColorSet() { + // As Painter's Servant enters the battlefield, choose a color. + // All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors. + addCard(Zone.HAND, playerA, "Painter's Servant", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + + addCard(Zone.HAND, playerB, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); + setChoice(playerA, "Blue"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Painter's Servant", 1); + + Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite()); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue()); + + silvercoatLion = getPermanent("Silvercoat Lion", playerB); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite()); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue()); + + for(Card card: playerA.getLibrary().getCards(currentGame)) { + Assert.assertEquals(card.getName() + " should be blue",true, card.getColor(currentGame).isBlue()); + } + for(Card card: playerB.getLibrary().getCards(currentGame)) { + Assert.assertEquals(card.getName() + " should be blue",true, card.getColor(currentGame).isBlue()); + } + + for(Card card: playerA.getHand().getCards(currentGame)) { + Assert.assertEquals(true, card.getColor(currentGame).isRed()); + Assert.assertEquals(true, card.getColor(currentGame).isBlue()); + } + for(Card card: playerB.getHand().getCards(currentGame)) { + Assert.assertEquals(true, card.getColor(currentGame).isRed()); + Assert.assertEquals(true, card.getColor(currentGame).isBlue()); + } + for(Card card: playerA.getGraveyard().getCards(currentGame)) { + Assert.assertEquals(true, card.getColor(currentGame).isWhite()); + Assert.assertEquals(true, card.getColor(currentGame).isBlue()); + } + for(Card card: playerB.getGraveyard().getCards(currentGame)) { + Assert.assertEquals(true, card.getColor(currentGame).isWhite()); + Assert.assertEquals(true, card.getColor(currentGame).isBlue()); + } + + } + + /** + * Test that the added color is no longer applied as Painter's Servant has left the battlefield + */ + @Test + public void testColorReset() { + // As Painter's Servant enters the battlefield, choose a color. + // All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors. + addCard(Zone.HAND, playerA, "Painter's Servant", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + + addCard(Zone.HAND, playerB, "Lightning Bolt",2); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + addCard(Zone.GRAVEYARD, playerB, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); + setChoice(playerA, "Blue"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Painter's Servant"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Painter's Servant", 1); + + Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite()); + Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isBlue()); + + silvercoatLion = getPermanent("Silvercoat Lion", playerB); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite()); + Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isBlue()); + + for(Card card: playerA.getLibrary().getCards(currentGame)) { + Assert.assertEquals(card.getName() + " should not be blue",false, card.getColor(currentGame).isBlue()); + } + for(Card card: playerB.getLibrary().getCards(currentGame)) { + Assert.assertEquals(card.getName() + " should not be blue",false, card.getColor(currentGame).isBlue()); + } + + for(Card card: playerA.getHand().getCards(currentGame)) { + Assert.assertEquals(true, card.getColor(currentGame).isRed()); + Assert.assertEquals(false, card.getColor(currentGame).isBlue()); + } + for(Card card: playerB.getHand().getCards(currentGame)) { + Assert.assertEquals(true, card.getColor(currentGame).isRed()); + Assert.assertEquals(false, card.getColor(currentGame).isBlue()); + } + for(Card card: playerA.getGraveyard().getCards(currentGame)) { + if(card.getName().equals("Silvercoat Lion")) { + Assert.assertEquals(true, card.getColor(currentGame).isWhite()); + Assert.assertEquals(false, card.getColor(currentGame).isBlue()); + } + } + for(Card card: playerB.getGraveyard().getCards(currentGame)) { + if(card.getName().equals("Silvercoat Lion")) { + Assert.assertEquals(true, card.getColor(currentGame).isWhite()); + Assert.assertEquals(false, card.getColor(currentGame).isBlue()); + } + } + + } + + /** + * 5/1/2008 While Painter's Servant is on the battlefield, an effect that + * changes an object's colors will overwrite Painter's Servant's effect. For + * example, casting Cerulean Wisps on a creature will turn it blue, + * regardless of the color chosen for Painter's Servant. + */ + @Test + public void testColorOverwrite() { + // As Painter's Servant enters the battlefield, choose a color. + // All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors. + addCard(Zone.HAND, playerA, "Painter's Servant", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + // Target creature becomes blue until end of turn. Untap that creature. + // Draw a card. + addCard(Zone.HAND, playerB, "Cerulean Wisps", 1); // Instant {U} + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); + setChoice(playerA, "Red"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Cerulean Wisps", "Silvercoat Lion", "Painter's Servant", StackClause.WHILE_NOT_ON_STACK); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Painter's Servant", 1); + + Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA); + Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isWhite()); + Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isRed()); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue()); + } + + /** + * Check color of spells + */ + @Test + public void testColorSpell() { + // As Painter's Servant enters the battlefield, choose a color. + // All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors. + addCard(Zone.HAND, playerA, "Painter's Servant", 1); + // Draw two cards. + addCard(Zone.HAND, playerA, "Divination", 1); // {U}{2} + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + // Whenever a player casts a red spell, you may gain 1 life. + addCard(Zone.BATTLEFIELD, playerA, "Dragon's Claw"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + // Target creature becomes blue until end of turn. Untap that creature. + // Draw a card. + addCard(Zone.HAND, playerB, "Cerulean Wisps", 1); // Instant {U} + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); + setChoice(playerA, "Red"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Cerulean Wisps", "Silvercoat Lion", "Painter's Servant", StackClause.WHILE_NOT_ON_STACK); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Divination", NO_TARGET, "Painter's Servant", StackClause.WHILE_NOT_ON_STACK); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Painter's Servant", 1); + + assertGraveyardCount(playerA, "Divination", 1); + assertGraveyardCount(playerB, "Cerulean Wisps", 1); + assertLife(playerA, 22); // + 1 from Cerulean Wisps + 1 from Divination + + Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA); + Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isWhite()); + Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isRed()); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue()); + + + } + + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SilenceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SilenceTest.java new file mode 100644 index 00000000000..80b18593084 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SilenceTest.java @@ -0,0 +1,63 @@ +/* + * 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 org.mage.test.cards.continuous; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class SilenceTest extends CardTestPlayerBase { + + @Test + public void testSilence() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.HAND, playerA, "Silence"); + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + addCard(Zone.HAND, playerB, "Silvercoat Lion", 1); + + castSpell(2, PhaseStep.UPKEEP, playerA, "Silence"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); + setStopAt(2, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertGraveyardCount(playerA, "Silence", 1); + + assertHandCount(playerB, "Silvercoat Lion", 1); + assertPermanentCount(playerB, "Silvercoat Lion", 0); + } + +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/TargetOpponentGainsControlTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/TargetOpponentGainsControlTest.java index 327079c36d2..9665a8c649e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/control/TargetOpponentGainsControlTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/TargetOpponentGainsControlTest.java @@ -31,14 +31,20 @@ public class TargetOpponentGainsControlTest extends CardTestPlayerBase { @Test public void testChangeControlEffectFromTwoCards() { addCard(Zone.HAND, playerA, "Lightning Bolt", 3); - addCard(Zone.HAND, playerA, "Unhallowed Pact", 3); - addCard(Zone.BATTLEFIELD, playerA, "Treacherous Pit-Dweller"); + // Enchant creature + // When enchanted creature dies, return that card to the battlefield under your control. + addCard(Zone.HAND, playerA, "Unhallowed Pact", 1); // {2}{B} + // Undying + // When Treacherous Pit-Dweller enters the battlefield from a graveyard, target opponent gains control of it. + addCard(Zone.BATTLEFIELD, playerA, "Treacherous Pit-Dweller"); // 4/3 addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unhallowed Pact", "Treacherous Pit-Dweller"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); + castSpell(1, PhaseStep.UPKEEP, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); // comes back with undying + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unhallowed Pact", "Treacherous Pit-Dweller"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); // Treacherous Pit-Dweller is now 5/4 castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); setStopAt(1, PhaseStep.END_TURN); @@ -46,8 +52,10 @@ public class TargetOpponentGainsControlTest extends CardTestPlayerBase { // went to graveyard assertGraveyardCount(playerA, "Unhallowed Pact", 1); + assertGraveyardCount(playerA, "Lightning Bolt", 3); // returned back - assertPermanentCount(playerA, "Treacherous Pit-Dweller", 1); + assertGraveyardCount(playerA, "Treacherous Pit-Dweller", 0); + assertPermanentCount(playerB, "Treacherous Pit-Dweller", 1); // opponent gets it because ETB of Dweller resolves always last } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java index 47bf48d5091..b02bbe4e383 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java @@ -108,9 +108,11 @@ public class CloneTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); addCard(Zone.BATTLEFIELD, playerB, "Swamp", 6); addCard(Zone.BATTLEFIELD, playerB, "Forest", 1); + // Target creature you control gets +1/+1 and gains hexproof until end of turn. (It can't be the target of spells or abilities your opponents control.) addCard(Zone.HAND, playerB, "Ranger's Guile"); addCard(Zone.HAND, playerA, "Clone"); + // Return target nonland permanent to its owner's hand. addCard(Zone.HAND, playerA, "Disperse"); addCard(Zone.BATTLEFIELD, playerB, "Nightmare", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java index a5b1a67587b..71506a20563 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java @@ -142,4 +142,39 @@ public class IsochronScepterTest extends CardTestPlayerBase { assertLife(playerB, 20); } + + /** + * Resolving a Silence cast from exile via Isochron Scepter during my opponent's upkeep does + * not prevent that opponent from casting spells that turn. + * + */ + + @Test + public void testSilence() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.HAND, playerA, "Isochron Scepter"); + addCard(Zone.HAND, playerA, "Silence"); + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + addCard(Zone.HAND, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Isochron Scepter"); + addTarget(playerA, "Silence"); + + activateAbility(2, PhaseStep.UPKEEP, playerA, "{2},{T}:"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Yes"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Isochron Scepter", 1); + assertExileCount("Silence", 1); + + assertHandCount(playerB, "Silvercoat Lion", 1); + assertPermanentCount(playerB, "Silvercoat Lion", 0); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java index 2d805bbd081..7b1ecd66b1e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java @@ -316,7 +316,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Frost Titan"); addCard(Zone.HAND, playerA, "Terror"); // {1}{U} - Target creature gains shroud until end of turn and can't be blocked this turn. - addCard(Zone.HAND, playerA, "Veil of Secrecy"); + addCard(Zone.HAND, playerA, "Veil of Secrecy"); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); addCard(Zone.BATTLEFIELD, playerA, "Island", 2); @@ -328,32 +328,31 @@ public class PhantasmalImageTest extends CardTestPlayerBase { setChoice(playerB, "Frost Titan"); castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Terror", "Frost Titan"); // of player Bs Phantasmal Image copying Frost Titan - // should be countered if not paying {2} + // should be countered if not paying {2} setStopAt(2, PhaseStep.END_TURN); execute(); assertGraveyardCount(playerA, "Veil of Secrecy", 1); assertGraveyardCount(playerA, "Terror", 1); - + assertLife(playerB, 20); assertLife(playerA, 20); - assertPermanentCount(playerA, "Frost Titan", 1); - + assertPermanentCount(playerA, "Frost Titan", 1); + assertGraveyardCount(playerB, "Phantasmal Image", 1); // if triggered ability did not work, the Titan would be in the graveyard instaed } - + // I've casted a Phantasmal Image targeting opponent's Wurmcoil Engine // When my Phantasmal Image died, it didn't triggered the Wurmcoil Engine's last ability // (When Wurmcoil Engine dies, put a 3/3 colorless Wurm artifact creature token with deathtouch and // a 3/3 colorless Wurm artifact creature token with lifelink onto the battlefield.) - @Test public void testDiesTriggeredAbilities() { addCard(Zone.BATTLEFIELD, playerA, "Wurmcoil Engine"); - // Destroy target artifact or enchantment. + // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. addCard(Zone.HAND, playerA, "Public Execution"); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); @@ -364,22 +363,212 @@ public class PhantasmalImageTest extends CardTestPlayerBase { setChoice(playerB, "Wurmcoil Engine"); castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Wurmcoil Engine"); // of player Bs Phantasmal Image copying Frost Titan - // should be countered if not paying {2} + // should be countered if not paying {2} setStopAt(2, PhaseStep.END_TURN); execute(); assertGraveyardCount(playerA, "Public Execution", 1); - + assertLife(playerB, 20); assertLife(playerA, 20); - - assertPermanentCount(playerA, "Wurmcoil Engine", 1); - + assertPermanentCount(playerA, "Wurmcoil Engine", 1); + assertGraveyardCount(playerB, "Phantasmal Image", 1); assertPermanentCount(playerB, "Wurm", 2); // if triggered ability did not work, the Titan would be in the graveyard instaed } - + + /** + * Phantasmal Image is not regestering Leave the battlefield triggers, + * persist and undying triggers + */ + @Test + public void testLeavesTheBattlefieldTriggeredAbilities() { + // Shadow (This creature can block or be blocked by only creatures with shadow.) + // When Thalakos Seer leaves the battlefield, draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Thalakos Seer"); + + // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. + addCard(Zone.HAND, playerA, "Public Execution"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + addCard(Zone.HAND, playerB, "Phantasmal Image"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted + setChoice(playerB, "Thalakos Seer"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Thalakos Seer"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Public Execution", 1); + + assertLife(playerB, 20); + assertLife(playerA, 20); + + assertPermanentCount(playerA, "Thalakos Seer", 1); + + assertGraveyardCount(playerB, "Phantasmal Image", 1); + + assertHandCount(playerB, 2); // 1 from draw turn 2 and 1 from Thalakos Seer leaves the battlefield trigger + } + + /** + * Action + * Game State 1 -----------------> Game State 2 + * (On 'field) (Move to GY) (In graveyard) + * + * LTB abilities such as Persist are expceptional in that they trigger based on their existence and + * state of objects before the event (Game State 1, when the card is on the battlefield) rather than + * after (Game State 2, when the card is in the graveyard). It doesn't matter that the LTB ability + * doesn't exist in Game State 2. [CR 603.6d] + * + * 603.6d Normally, objects that exist immediately after an event are checked to see if the event matched any trigger conditions. + * Continuous effects that exist at that time are used to determine what the trigger conditions are and what the objects involved + * in the event look like. However, some triggered abilities must be treated specially. Leaves-the-battlefield abilities, abilities + * that trigger when a permanent phases out, abilities that trigger when an object that all players can see is put into a hand or + * library, abilities that trigger specifically when an object becomes unattached, abilities that trigger when a player loses control + * of an object, and abilities that trigger when a player planeswalks away from a plane will trigger based on their existence, and + * the appearance of objects, prior to the event rather than afterward. The game has to “look back in time” to determine if these abilities trigger. + * + * Example: Two creatures are on the battlefield along with an artifact that has the ability “Whenever a creature dies, you gain 1 life.” + * Someone plays a spell that destroys all artifacts, creatures, and enchantments. The artifact’s ability triggers twice, even though + * the artifact goes to its owner’s graveyard at the same time as the creatures. + * + */ + @Test + public void testPersist() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + // When Kitchen Finks enters the battlefield, you gain 2 life. + // 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, "Kitchen Finks"); + + // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. + addCard(Zone.HAND, playerA, "Public Execution"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + + // You may have Phantasmal Image enter the battlefield as a copy of any creature + // on the battlefield, except it's an Illusion in addition to its other types and + // it gains "When this creature becomes the target of a spell or ability, sacrifice it." + addCard(Zone.HAND, playerB, "Phantasmal Image"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitchen Finks"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted + setChoice(playerB, "Kitchen Finks"); + + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Kitchen Finks"); + setChoice(playerB, "Kitchen Finks"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Public Execution", 1); + + assertLife(playerA, 22); + assertLife(playerB, 24); + + assertPermanentCount(playerA, "Kitchen Finks", 1); + + assertHandCount(playerB, "Phantasmal Image", 0); + assertGraveyardCount(playerB, "Phantasmal Image", 0); + assertPermanentCount(playerB, "Kitchen Finks", 1); + assertPowerToughness(playerB, "Kitchen Finks", 2, 1); + + } + + @Test + public void testUndying() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Undying (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, "Butcher Ghoul"); + + // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. + addCard(Zone.HAND, playerA, "Public Execution"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + + // You may have Phantasmal Image enter the battlefield as a copy of any creature + // on the battlefield, except it's an Illusion in addition to its other types and + // it gains "When this creature becomes the target of a spell or ability, sacrifice it." + addCard(Zone.HAND, playerB, "Phantasmal Image"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Butcher Ghoul"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted + setChoice(playerB, "Butcher Ghoul"); + + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Butcher Ghoul"); + setChoice(playerB, "Butcher Ghoul"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Public Execution", 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPermanentCount(playerA, "Butcher Ghoul", 1); + + assertHandCount(playerB, "Phantasmal Image", 0); + assertGraveyardCount(playerB, "Phantasmal Image", 0); + assertPermanentCount(playerB, "Butcher Ghoul", 1); + assertPowerToughness(playerB, "Butcher Ghoul", 2, 2); + + } + + /** + * 12:29: Attacker: Wurmcoil Engine [466] (6/6) blocked by Wurmcoil Engine + * [4ed] (6/6) + * 12:29: yespair gains 6 life + * 12:29: HipSomHap gains 6 life + * 12:29: Wurmcoil Engine [4ed] died + * 12:29: Ability triggers: Wurmcoil Engine [4ed] - When Wurmcoil Engine [4ed] dies, put a a 3/3 colorless + * Wurm artifact creature token with deathtouch onto the battlefield. Put a + * a 3/3 colorless Wurm artifact creature token with lifelink onto the + * battlefield. + * 12:29: Phantasmal Image [466] died + * 12:29: HipSomHap puts a Wurm [7d0] token onto the battlefield + * 12:29: HipSomHap puts a Wurm [186] token onto the battlefield + * + * To the best of my knowledge, the Phantasmal Image [466], which entered + * the battlefield as a Wurmcoil Engine, should grant tokens through the + * Dies-trigger as well, right? + */ + + @Test + public void testDiesTriggered2() { + addCard(Zone.BATTLEFIELD, playerB, "Wurmcoil Engine"); + + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.HAND, playerA, "Phantasmal Image"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phantasmal Image"); // not targeted + setChoice(playerB, "Wurmcoil Engine"); + + attack(2, playerB, "Wurmcoil Engine"); + block(2, playerA, "Wurmcoil Engine", "Wurmcoil Engine"); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 26); + assertLife(playerA, 26); + + assertGraveyardCount(playerA, "Phantasmal Image", 1); + assertGraveyardCount(playerB, "Wurmcoil Engine", 1); + + assertPermanentCount(playerA, "Wurm", 2); + assertPermanentCount(playerB, "Wurm", 2); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VesuvaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VesuvaTest.java new file mode 100644 index 00000000000..601be2d21f4 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VesuvaTest.java @@ -0,0 +1,105 @@ +/* + * 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 org.mage.test.cards.copy; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class VesuvaTest extends CardTestPlayerBase { + + /** + * played a Vesuva on Glimmerpost and since when Vesuva becames Glimmerpost + * it is already on the battlefield the ability won't trigger. Then when i + * used Vesuva to copy an opponent's Dark Depth i got the land with the 10 + * counters. + * + * If Dark Depth says "it enters the battlefield with 10 counter" but Vesuva + * is already on the field shouldnt have any counters or the problem was + * that Glimmerpost should have trigger? + */ + @Test + public void testGlimmerpost() { + // When Glimmerpost enters the battlefield, you gain 1 life for each Locus on the battlefield. + // {T}: {1} Add to your mana pool. + addCard(Zone.HAND, playerA, "Glimmerpost", 1); + // You may have Vesuva enter the battlefield tapped as a copy of any land on the battlefield. + addCard(Zone.HAND, playerA, "Vesuva", 1); + + addCard(Zone.HAND, playerB, "Glimmerpost", 1); + + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Glimmerpost"); + playLand(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Glimmerpost"); + playLand(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Vesuva"); + setChoice(playerA, "Glimmerpost"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Glimmerpost", 2); + assertPermanentCount(playerB, "Glimmerpost", 1); + + assertLife(playerA, 24); // 20 + 1 + 3 + assertLife(playerB, 22); // 20 + 2 + } + + @Test + public void testDarkDepth() { + // Dark Depths enters the battlefield with ten ice counters on it. + // {3}: Remove an ice counter from Dark Depths. + // When Dark Depths has no ice counters on it, sacrifice it. If you do, put a legendary 20/20 black Avatar creature token with flying and "This creature is indestructible" named Marit Lage onto the battlefield. + addCard(Zone.BATTLEFIELD, playerB, "Dark Depths", 1); + + // You may have Vesuva enter the battlefield tapped as a copy of any land on the battlefield. + addCard(Zone.HAND, playerA, "Vesuva", 1); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vesuva"); + setChoice(playerA, "Dark Depths"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Dark Depths", 1); + assertPermanentCount(playerB, "Dark Depths", 1); + + Permanent darkDepth = getPermanent("Dark Depths", playerA); + if (darkDepth != null) { + Assert.assertEquals(darkDepth.getCounters().getCount("ice"), 10); + } + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/RemoveCounterCostTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/RemoveCounterCostTest.java new file mode 100644 index 00000000000..8a271fad85e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/RemoveCounterCostTest.java @@ -0,0 +1,65 @@ +/* + * 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 org.mage.test.cards.cost.additional; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class RemoveCounterCostTest extends CardTestPlayerBase { + + @Test + public void testNovijenSages() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 7); + // Graft 4 + // {1}, Remove two +1/+1 counters from among creatures you control: Draw a card. + addCard(Zone.HAND, playerA, "Novijen Sages"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Novijen Sages"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1},Remove two +1/+1 counters"); + setChoice(playerA, "X=2"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Novijen Sages", 1); + assertPowerToughness(playerA, "Novijen Sages", 2, 2); + + assertHandCount(playerA, 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/SewerNemesisTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/SewerNemesisTest.java index 37abc2148ea..6ca2b46617a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/SewerNemesisTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/SewerNemesisTest.java @@ -41,12 +41,16 @@ public class SewerNemesisTest extends CardTestPlayerBase { /** - * Sewer Nemesis count's all cards in each player's graveyard to determine it's * / *, not just the cosen player's graveyard. + * BUG: Sewer Nemesis count's all cards in each player's graveyard to determine it's * / *, not just the chosen player's graveyard. * */ @Test public void test1() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + + // As Sewer Nemesis enters the battlefield, choose a player. + // Sewer Nemesis's power and toughness are each equal to the number of cards in the chosen player's graveyard. + // Whenever the chosen player casts a spell, that player puts the top card of his or her library into his or her graveyard. addCard(Zone.HAND, playerA, "Sewer Nemesis"); addCard(Zone.GRAVEYARD, playerA, "Raging Goblin",4); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaFlareTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaFlareTest.java new file mode 100644 index 00000000000..f81242ffc2b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaFlareTest.java @@ -0,0 +1,90 @@ +/* + * 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 org.mage.test.cards.mana; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class ManaFlareTest extends CardTestPlayerBase { + + @Test + public void testIsland() { + // Whenever a player taps a land for mana, that player adds one mana to his or her mana pool of any type that land produced. + addCard(Zone.BATTLEFIELD, playerA, "Mana Flare", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + + // Creature {U}{U} + // {U},{T} :Return target permanent you control to its owner's hand. + addCard(Zone.HAND, playerA, "Vedalken Mastermind", 1); + + // because available mana calculation does not work correctly with Mana Flare we have to tap the land manually + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U} to your mana pool"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vedalken Mastermind"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Vedalken Mastermind", 1); + + } + + + /** + * Mana Flare is only adding colorless mana, at least off of dual lands (Watery Grave in this instance). + * Island only adds colorless. Plains adds white though. + */ + @Test + public void testWateryGrave() { + // {T}: Add one mana of any color to your mana pool. Spend this mana only to cast a multicolored spell. + addCard(Zone.BATTLEFIELD, playerB, "Mana Flare", 1); + addCard(Zone.BATTLEFIELD, playerB, "Watery Grave", 1); + + // Creature {B}{B} + // {B}: Nantuko Shade gets +1/+1 until end of turn. + addCard(Zone.HAND, playerB, "Nantuko Shade", 1); + + // because available mana calculation does not work correctly with Mana Flare we have to tap the land manually + activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add {B} to your mana pool"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Nantuko Shade"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Nantuko Shade", 1); + + } + + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java new file mode 100644 index 00000000000..20b7bdec98d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.replacement; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class LeylineOfTheVoidTest extends CardTestPlayerBase { + + /** + * Leyline of the Void is on the battlefield, Helm of Obedience is used on + * an opponent with X=1. Now the Helm states to mill "until a creature card + * or X cards are put into the graveyard this way". For each of those milled + * cards, Leyline states to "remove [them] from the game instead". In other + * words: Leyline + Helm and X of at least 1 exiles the library of one + * unlucky opponent. Or should, because right now it doesn't. Right now it's + * Helm's originally intended "X or 1st Creature" amount of cards that after + * it's effect get exiled. + */ + @Test + public void testGrindstoneProgenius() { + addCard(Zone.BATTLEFIELD, playerA, "Plains"); + + // If Leyline of the Void is in your opening hand, you may begin the game with it on the battlefield. + // If a card would be put into an opponent's graveyard from anywhere, exile it instead. + addCard(Zone.BATTLEFIELD, playerA, "Leyline of the Void"); + + // {X}, {T}: Target opponent puts cards from the top of his or her library into his or her graveyard until a creature card or X cards are put into that graveyard this way, whichever comes first. If a creature card is put into that graveyard this way, sacrifice Helm of Obedience and put that card onto the battlefield under your control. X can't be 0. + addCard(Zone.BATTLEFIELD, playerA, "Helm of Obedience"); + + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{X},{T}: Target opponent puts cards", playerB); + setChoice(playerA, "X=1"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertExileCount(playerB, 71); // All cards go to exile replaced from Leyline of the void + } + +} + + diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/SigardaHostOfHeronsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/SigardaHostOfHeronsTest.java index e02c0c7ed97..3148f858b2e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/SigardaHostOfHeronsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/SigardaHostOfHeronsTest.java @@ -18,18 +18,25 @@ public class SigardaHostOfHeronsTest extends CardTestPlayerBase { */ @Test public void testCard() { + // Spells and abilities your opponents control can't cause you to sacrifice permanents. addCard(Zone.BATTLEFIELD, playerA, "Sigarda, Host of Herons"); + // {T}, Tap two untapped Humans you control: Exile target artifact or enchantment. addCard(Zone.BATTLEFIELD, playerA, "Devout Chaplain"); + // {2}{B}, Sacrifice a creature: Target opponent reveals his or her hand. You choose a card from it. That player discards that card. Activate this ability only any time you could cast a sorcery. addCard(Zone.BATTLEFIELD, playerA, "Corpse Traders"); + // Target player sacrifices a creature. addCard(Zone.HAND, playerA, "Diabolic Edict"); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); - + addCard(Zone.HAND, playerB, "Diabolic Edict"); addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + + // At the beginning of your upkeep, return target creature card from your graveyard to the battlefield. + // At the beginning of each opponent's upkeep, that player sacrifices a creature. addCard(Zone.BATTLEFIELD, playerB, "Sheoldred, Whispering One"); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Diabolic Edict", playerA); - castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Diabolic Edict", playerB); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Diabolic Edict", playerA); // sacrificing for player A prevented by Sigarda + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Diabolic Edict", playerB); // playerB has to sacrifice Sheldred setStopAt(3, PhaseStep.END_TURN); execute(); @@ -40,9 +47,13 @@ public class SigardaHostOfHeronsTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Sigarda, Host of Herons", 1); assertPermanentCount(playerA, "Devout Chaplain", 1); assertPermanentCount(playerA, "Corpse Traders", 1); + assertGraveyardCount(playerA, "Diabolic Edict", 1); assertGraveyardCount(playerA, 1); assertPermanentCount(playerB, "Sheoldred, Whispering One", 0); + assertHandCount(playerB, "Diabolic Edict", 0); + assertGraveyardCount(playerB, "Sheoldred, Whispering One", 1); + assertGraveyardCount(playerB, "Diabolic Edict", 1); assertGraveyardCount(playerB, 2); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java index 49ccc89860c..441d68754b9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java @@ -14,7 +14,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class TorporOrbTest extends CardTestPlayerBase { @Test - public void testCard() { + public void testWallOfOmens() { addCard(Zone.BATTLEFIELD, playerA, "Torpor Orb"); addCard(Zone.HAND, playerA, "Wall of Omens"); addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); @@ -32,4 +32,30 @@ public class TorporOrbTest extends CardTestPlayerBase { assertHandCount(playerA, 0); } + /** + * Treacherous Pit-Dweller doesnt function properly with Torpor Orb and Hushwing Gryff + */ + @Test + public void testPitTweller() { + addCard(Zone.BATTLEFIELD, playerB, "Torpor Orb"); + addCard(Zone.BATTLEFIELD, playerB, "Treacherous Pit-Dweller"); // 4/3 + + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + attack(2, playerB, "Treacherous Pit-Dweller"); + castSpell(2, PhaseStep.DECLARE_ATTACKERS, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + + assertPermanentCount(playerB, "Treacherous Pit-Dweller", 1); + assertPowerToughness(playerB, "Treacherous Pit-Dweller", 5,4); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/WinLoseEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/WinLoseEffectsTest.java index d7ee8020536..41f230092b2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/WinLoseEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/WinLoseEffectsTest.java @@ -93,4 +93,55 @@ public class WinLoseEffectsTest extends CardTestPlayerBase { assertLife(playerB, 20); } + + @Test + public void testAngelsGrace2() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + // Instant {W} + // You can't lose the game this turn and your opponents can't win the game this turn. Until end of turn, damage that would reduce your life total to less than 1 reduces it to 1 instead. + addCard(Zone.HAND, playerA, "Angel's Grace"); + // Instant - {3}{B}{B} + // Reveal the top card of your library and put that card into your hand. You lose life equal to its converted mana cost. + // You may repeat this process any number of times. + addCard(Zone.HAND, playerA, "Ad Nauseam"); + + // Creature + // If you would draw a card while your library has no cards in it, you win the game instead. + addCard(Zone.BATTLEFIELD, playerA, "Laboratory Maniac", 1); + + skipInitShuffling(); + + playerA.getLibrary().clear(); + // Instant {U} + // Draw a card. Scry 2 + addCard(Zone.LIBRARY, playerA, "Serum Visions"); // 1 life lost + addCard(Zone.LIBRARY, playerA, "Bogardan Hellkite", 3); // 24 life lost + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel's Grace"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ad Nauseam"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Yes"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Serum Visions"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Angel's Grace", 1); + assertGraveyardCount(playerA, "Ad Nauseam", 1); + assertGraveyardCount(playerA, "Serum Visions", 1); + + Assert.assertEquals("Player A library is empty", 0 , playerA.getLibrary().size()); + + assertLife(playerA, -5); + assertLife(playerB, 20); + + Assert.assertTrue("Player A has not won but should have", playerA.hasWon()); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/RefractionTrapTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/RefractionTrapTest.java new file mode 100644 index 00000000000..4b013c46cad --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/RefractionTrapTest.java @@ -0,0 +1,78 @@ +/* + * 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 org.mage.test.cards.replacement.prevent; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class RefractionTrapTest extends CardTestPlayerBase { + + /** + * I found a bug when i cast Refraction Trap I choose to redirect damage to + * opponent creature and it stays alive (in 2 situations) + */ + @Test + public void testPreventDamageFromSpell() { + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + // If an opponent cast a red instant or sorcery spell this turn, you may pay {W} + // rather than pay Refraction Trap's mana cost. + // Prevent the next 3 damage that a source of your choice would deal to you and/or + // permanents you control this turn. If damage is prevented this way, Refraction Trap + // deals that much damage to target creature or player. + addCard(Zone.HAND, playerB, "Refraction Trap"); + addCard(Zone.BATTLEFIELD, playerB, "Plains"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Refraction Trap", "Silvercoat Lion", "Lightning Bolt"); + setChoice(playerB, "Yes"); + setChoice(playerB, "Lightning Bolt"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertGraveyardCount(playerB, "Refraction Trap", 1); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + + } + +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantAttackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantAttackTest.java new file mode 100644 index 00000000000..479e09910a9 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantAttackTest.java @@ -0,0 +1,71 @@ +/* + * 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 org.mage.test.cards.restriction; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class CantAttackTest extends CardTestPlayerBase { + + /** + * Tests "If all other elves get the Forestwalk ability and can't be blockt from creatures whose controler has a forest in game" + */ + + @Test + public void testAttack() { + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // 2/2 + addCard(Zone.BATTLEFIELD, playerA, "Myr Enforcer"); // 4/4 + + addCard(Zone.BATTLEFIELD, playerB, "Akron Legionnaire"); // 8/4 + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); // 2/2 + addCard(Zone.BATTLEFIELD, playerB, "Myr Enforcer"); // 4/4 + + attack(2, playerB, "Akron Legionnaire"); + attack(2, playerB, "Silvercoat Lion"); + attack(2, playerB, "Myr Enforcer"); + + attack(3, playerA, "Silvercoat Lion"); + attack(3, playerA, "Myr Enforcer"); + + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 8); // 8 + 4 + assertLife(playerB, 14); // 4 + 2 + + } + +} + diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/MisdirectionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/MisdirectionTest.java index 737f8cd5f82..bbb375a2ccb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/MisdirectionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/MisdirectionTest.java @@ -68,6 +68,74 @@ public class MisdirectionTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Rakshasa's Secret", 1); assertGraveyardCount(playerB, "Misdirection", 1); assertHandCount(playerB, "Silvercoat Lion", 0); - } + + // check to change target permanent creature legal to to a creature the opponent of the spell controller controls + @Test + public void testChangePublicExecution() { + // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. + addCard(Zone.HAND, playerA, "Public Execution"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + /* + Misdirection {3}{U}{U} + Instant + You may exile a blue card from your hand rather than pay Misdirection's mana cost. + Change the target of target spell with a single target. + */ + addCard(Zone.HAND, playerB, "Misdirection"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1); + addCard(Zone.BATTLEFIELD, playerB, "Custodian of the Trove", 1); // 4/3 + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Public Execution", "Pillarfield Ox"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Misdirection", "Public Execution", "Public Execution"); + addTarget(playerB, "Custodian of the Trove"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Public Execution", 1); + assertGraveyardCount(playerB, "Misdirection", 1); + + assertGraveyardCount(playerB, "Custodian of the Trove",1); + assertPermanentCount(playerB, "Pillarfield Ox", 1); + assertPowerToughness(playerB, "Pillarfield Ox", 0, 4); + + } + + // check to change target permanent creature not legal to to a creature the your opponent controls + @Test + public void testChangePublicExecution2() { + // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. + addCard(Zone.HAND, playerA, "Public Execution"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + addCard(Zone.BATTLEFIELD, playerA, "Keeper of the Lens", 1); + /* + Misdirection {3}{U}{U} + Instant + You may exile a blue card from your hand rather than pay Misdirection's mana cost. + Change the target of target spell with a single target. + */ + addCard(Zone.HAND, playerB, "Misdirection"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1); + addCard(Zone.BATTLEFIELD, playerB, "Custodian of the Trove", 1); // 4/3 + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Public Execution", "Custodian of the Trove"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Misdirection", "Public Execution", "Public Execution"); + addTarget(playerB, "Keeper of the Lens"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Public Execution", 1); + assertGraveyardCount(playerB, "Misdirection", 1); + assertPermanentCount(playerA, "Keeper of the Lens", 1); + + assertPermanentCount(playerB, "Pillarfield Ox", 1); + assertPowerToughness(playerB, "Pillarfield Ox", 0, 4); + + assertGraveyardCount(playerB, "Custodian of the Trove",1); + + } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/KeranosGodOfStormsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/KeranosGodOfStormsTest.java new file mode 100644 index 00000000000..99cc3c7b74f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/KeranosGodOfStormsTest.java @@ -0,0 +1,105 @@ +/* + * 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 org.mage.test.cards.single.ths; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class KeranosGodOfStormsTest extends CardTestPlayerBase { + + @Test + public void testKeranosNormal() { + // Reveal the first card you draw on each of your turns. + // Whenever you reveal a land card this way, draw a card. + // Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player. + addCard(Zone.BATTLEFIELD, playerB, "Keranos, God of Storms"); // {3}{U}{R} + // Look at target player's hand. + // Draw a card. + addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 3); + skipInitShuffling(); + + addTarget(playerB, playerA); // damage from Keranos trigger + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, "Keranos, God of Storms", 1); + + assertHandCount(playerB, "Silvercoat Lion", 1); // 1 Lion from the draw and 1 Lion from Peek + + assertLife(playerA, 17); + assertLife(playerB, 20); + + } + + /** + * Keranos, God of Storms, will look at the first card drawn after he is + * played, not the first card drawn each turn. + * + * My opponent, draws, plays Keranos, then plays Stroke of Genius. Keranos + * triggers on the first card for Stroke of Genius. + */ + @Test + public void testKeranosCastAfterFirstDraw() { + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + // Reveal the first card you draw on each of your turns. + // Whenever you reveal a land card this way, draw a card. + // Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player. + addCard(Zone.HAND, playerB, "Keranos, God of Storms"); // {3}{U}{R} + // Look at target player's hand. + // Draw a card. + addCard(Zone.HAND, playerB, "Peek"); + + addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 3); + skipInitShuffling(); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Keranos, God of Storms"); + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Peek", playerA); // you won't do damage because it's not the first draw this turn - Draw in draw phase was the first + addTarget(playerB, playerA); // not needed if it works correct + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, "Keranos, God of Storms", 1); + assertGraveyardCount(playerB, "Peek", 1); + + assertHandCount(playerB, "Silvercoat Lion", 2); // 1 Lion from the draw and 1 Lion from Peek + + assertLife(playerA, 20); + assertLife(playerB, 20); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/OrdealEnchantmentsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/OrdealEnchantmentsTest.java new file mode 100644 index 00000000000..fb492135112 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/OrdealEnchantmentsTest.java @@ -0,0 +1,74 @@ +/* + * 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 org.mage.test.cards.single.ths; + +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class OrdealEnchantmentsTest extends CardTestPlayerBase { + + @Test + public void testOrdealofHeliod() { + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + // Enchant creature + // Whenever enchanted creature attacks, put a +1/+1 counter on it. Then if it has three or more +1/+1 counters on it, sacrifice Ordeal of Heliod. + // When you sacrifice Ordeal of Heliod, you gain 10 life. + addCard(Zone.HAND, playerB, "Ordeal of Heliod"); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Ordeal of Heliod", "Silvercoat Lion"); + + attack(2, playerB, "Silvercoat Lion"); + attack(4, playerB, "Silvercoat Lion"); + attack(6, playerB, "Silvercoat Lion"); + + setStopAt(6, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerA, 8); // 3 + 4 + 5 = 12 + assertLife(playerB, 30); + + assertGraveyardCount(playerB, "Ordeal of Heliod", 1); + + assertPermanentCount(playerB, "Silvercoat Lion", 1); + assertPowerToughness(playerB, "Silvercoat Lion", 5,5); + } + + +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BurningEarthTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BurningEarthTest.java new file mode 100644 index 00000000000..21e6af173df --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BurningEarthTest.java @@ -0,0 +1,71 @@ +/* + * 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 org.mage.test.cards.triggers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class BurningEarthTest extends CardTestPlayerBase { + + /** + * Burning Earth - It doesn't cause the damage it should. My opponent taps a + * Blood Crypt and an Overgrown Tomb for black and green mana respectively + * and casts his card all the while without taking any damage. + * + */ + @Test + public void testBurningEarth() { + // Destroy target artifact or creature. It can't be regenerated. + addCard(Zone.HAND, playerB, "Putrefy"); // {1}{B}{G} + addCard(Zone.BATTLEFIELD, playerB, "Darksteel Citadel", 1); + addCard(Zone.BATTLEFIELD, playerB, "Blood Crypt", 1); // {B}{R} + addCard(Zone.BATTLEFIELD, playerB, "Overgrown Tomb", 1); // {B}{G} + + // Whenever a player taps a nonbasic land for mana, Burning Earth deals 1 damage to that player. + addCard(Zone.BATTLEFIELD, playerA, "Burning Earth", 1); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Putrefy", "Silvercoat Lion"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, "Putrefy", 1); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + + assertLife(playerA, 20); + assertLife(playerB, 17); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToBattlefieldUnderYourControlTargetEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToBattlefieldEffectsTest.java similarity index 71% rename from Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToBattlefieldUnderYourControlTargetEffectTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToBattlefieldEffectsTest.java index fee25268faf..dc6b5f32a59 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToBattlefieldUnderYourControlTargetEffectTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToBattlefieldEffectsTest.java @@ -37,7 +37,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * @author LevelX2 */ -public class ReturnToBattlefieldUnderYourControlTargetEffectTest extends CardTestPlayerBase { +public class ReturnToBattlefieldEffectsTest extends CardTestPlayerBase { @Test public void testSaffiEriksdotter() { @@ -115,5 +115,41 @@ public class ReturnToBattlefieldUnderYourControlTargetEffectTest extends CardTes assertExileCount("Arcbound Worker", 1); } - + /** + * With my opponent's Deathmist Raptor return-to-battlefield trigger on the stack, + * I exiled the Deathmist Raptor with Pharika, God of Affliction. However, the Deathmist Raptor + * returned to the battlefield from exile, though it should not have because it had + * changed zones so was a different object. + */ + @Test + public void testDeathmistRaptor() { + // Deathtouch + // Whenever a permanent you control is turned face up, you may return Deathmist Raptor from your graveyard to the battlefield face up or face down. + // Megamorph {4}{G} + addCard(Zone.GRAVEYARD, playerA, "Deathmist Raptor"); + addCard(Zone.HAND, playerA, "Pine Walker"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + + addCard(Zone.BATTLEFIELD, playerB, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 1); + // {B}{G}: Exile target creature card from a graveyard. It's owner puts a 1/1 black and green Snake enchantment creature token with deathtouch onto the battlefield. + addCard(Zone.BATTLEFIELD, playerB, "Pharika, God of Affliction", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pine Walker"); + setChoice(playerA, "Yes"); // cast it face down as 2/2 creature + + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "{4}{G}: Turn this face-down permanent face up."); + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerB, "{B}{G}: Exile target creature card from a graveyard", + "Deathmist Raptor", "Whenever a permanent you control is turned face up"); + + setStopAt(3, PhaseStep.END_TURN); + + execute(); + assertPermanentCount(playerB, "Pharika, God of Affliction", 1); + assertPermanentCount(playerA, "Snake", 1); + assertPermanentCount(playerA, "Pine Walker", 1); + + assertExileCount("Deathmist Raptor", 1); + + } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToHandEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToHandEffectsTest.java new file mode 100644 index 00000000000..9159d14fd9d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToHandEffectsTest.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 org.mage.test.cards.triggers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class ReturnToHandEffectsTest extends CardTestPlayerBase { + + /** + * Enduring Renewal doesn't return creatures to hand put into graveyard from + * the battlefield It happened with Enduring Renewal in the battlefield + * while feeding Ornithopter to Grinding Station + */ + @Test + public void testEnduringRenewal() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // Play with your hand revealed. + // If you would draw a card, reveal the top card of your library instead. If it's a creature card, put it into your graveyard. Otherwise, draw a card. + // Whenever a creature is put into your graveyard from the battlefield, return it to your hand. + addCard(Zone.BATTLEFIELD, playerA, "Enduring Renewal"); + + // {T}, Sacrifice an artifact: Target player puts the top three cards of his or her library into his or her graveyard. + // Whenever an artifact enters the battlefield, you may untap Grinding Station. + addCard(Zone.BATTLEFIELD, playerA, "Grinding Station", 1); + addCard(Zone.BATTLEFIELD, playerA, "Ornithopter", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice an artifact", playerB); + // addTarget(playerA, "Ornithopter"); + setChoice(playerA, "Ornithopter"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, 3); + assertHandCount(playerA, "Ornithopter", 1); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SpellCastTriggerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SpellCastTriggerTest.java index 97da7f949d1..ce7aaa7db61 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SpellCastTriggerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SpellCastTriggerTest.java @@ -65,4 +65,32 @@ public class SpellCastTriggerTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Sunscorch Regent", 5, 4); } -} \ No newline at end of file + /** + * Monastery Mentor triggers are causing a "rollback" error. + */ + @Test + public void testMonasteryMentor() { + // Prowess (Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.) + // Whenever you cast a noncreature spell, put a 1/1 white Monk creature token with prowess onto the battlefield. + addCard(Zone.BATTLEFIELD, playerA, "Monastery Mentor", 1); + + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 14); + + assertGraveyardCount(playerA, "Lightning Bolt", 2); + assertPermanentCount(playerA, "Monk", 2); + assertPowerToughness(playerA, "Monk", 2, 2); + assertPowerToughness(playerA, "Monk", 1, 1); + + assertPowerToughness(playerA, "Monastery Mentor", 4, 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/state/SynodCenturionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/state/SynodCenturionTest.java index 27043288f72..03ecd3287aa 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/state/SynodCenturionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/state/SynodCenturionTest.java @@ -25,11 +25,15 @@ public class SynodCenturionTest extends CardTestPlayerBase { @Test public void testAlone() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + // Whenever a player casts a black spell, you may gain 1 life. addCard(Zone.BATTLEFIELD, playerA, "Demon's Horn"); + // Destroy target artifact. addCard(Zone.HAND, playerA, "Shatter"); + // When you control no other artifacts, sacrifice Synod Centurion. addCard(Zone.HAND, playerA, "Synod Centurion"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Synod Centurion"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Shatter", "Demon's Horn"); setStopAt(1, PhaseStep.END_TURN); diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java new file mode 100644 index 00000000000..33bbc816b54 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java @@ -0,0 +1,54 @@ +/* + * 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 org.mage.test.commander.duel; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * + * @author LevelX2 + */ +public class CastCommanderTest extends CardTestCommanderDuelBase { + @Test + public void testFirstAbility() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ob Nixilis of the Black Oath"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 40); + assertLife(playerB, 40); + + assertPermanentCount(playerA, "Ob Nixilis of the Black Oath", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java new file mode 100644 index 00000000000..b83ad522a16 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java @@ -0,0 +1,68 @@ +/* + * 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 org.mage.test.commander.duel; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * + * @author LevelX2 + */ + +public class OpalPalaceTest extends CardTestCommanderDuelBase { + /** + * I cast my commander with Opal Palace's second ability and it did not receive a +1/+1 counter + * the first time it was cast (rulings say it should on the first time cast). + */ + @Test + public void testFirstAbility() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + // {T}: Add {1} to your mana pool. + // {1}, {T}: Add to your mana pool one mana of any color in your commander's color identity. + // If you spend this mana to cast your commander, it enters the battlefield with a number of +1/+1 counters on it + // equal to the number of times it's been cast from the command zone this game. + addCard(Zone.BATTLEFIELD, playerA, "Opal Palace", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ob Nixilis of the Black Oath"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 40); + assertLife(playerB, 40); + + assertPermanentCount(playerA, "Ob Nixilis of the Black Oath", 1); + assertCounterCount("Ob Nixilis of the Black Oath", CounterType.P1P1, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/BloodchiefAscensionTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/BloodchiefAscensionTest.java new file mode 100644 index 00000000000..abad3873732 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/BloodchiefAscensionTest.java @@ -0,0 +1,146 @@ +/* + * 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 org.mage.test.multiplayer; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestMultiPlayerBase; + +/** + * + * @author LevelX2 + */ +public class BloodchiefAscensionTest extends CardTestMultiPlayerBase { + + @Test + public void testBloodchiefAscensionAllPlayers() { + // Enchantment + // At the beginning of each end step, if an opponent lost 2 or more life this turn, you may put a quest counter on Bloodchief Ascension. (Damage causes loss of life.) + // Whenever a card is put into an opponent's graveyard from anywhere, if Bloodchief Ascension has three or more quest counters on it, you may have that player lose 2 life. If you do, you gain 2 life. + addCard(Zone.BATTLEFIELD, playerA, "Bloodchief Ascension"); + + addCard(Zone.BATTLEFIELD, playerA, "Mountain",3); + addCard(Zone.HAND, playerA, "Fireball"); + addCard(Zone.BATTLEFIELD, playerD, "Mountain",3); + addCard(Zone.HAND, playerD, "Fireball"); + addCard(Zone.BATTLEFIELD, playerC, "Mountain",3); + addCard(Zone.HAND, playerC, "Fireball"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain",3); + addCard(Zone.HAND, playerB, "Fireball"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fireball", playerA); + setChoice(playerA, "X=2"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Fireball", playerD); + setChoice(playerD, "X=2"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Fireball", playerC); + setChoice(playerC, "X=2"); + + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Fireball", playerB); + setChoice(playerB, "X=2"); + + // Player order: A -> D -> C -> B + + setStopAt(4, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 18); + assertLife(playerB, 18); + assertLife(playerC, 18); + assertLife(playerD, 18); + + assertGraveyardCount(playerA, "Fireball", 1); + assertGraveyardCount(playerB, "Fireball", 1); + assertGraveyardCount(playerC, "Fireball", 1); + assertGraveyardCount(playerD, "Fireball", 1); + + + + assertCounterCount("Bloodchief Ascension", CounterType.QUEST, 2); // 1 opponent out of range + + } + /** + * One of my opponents in a multiplayer game had a Bloodchief Ascension in play. I took lethal damage on my turn, + * but he didn't get a counter on Bloodchief Ascension at my end step. I think he should, even though I had left + * the game from dying, because of: + * + * 800.4g. If a player leaves the game during his or her turn, that turn continues to its completion without an + * active player. If the active player would receive priority, instead the next player in turn order receives priority, + * or the top object on the stack resolves, or the phase or step ends, whichever is appropriate. + */ + @Test + public void testBloodchiefAscension() { + // Enchantment + // At the beginning of each end step, if an opponent lost 2 or more life this turn, you may put a quest counter on Bloodchief Ascension. (Damage causes loss of life.) + // Whenever a card is put into an opponent's graveyard from anywhere, if Bloodchief Ascension has three or more quest counters on it, you may have that player lose 2 life. If you do, you gain 2 life. + addCard(Zone.BATTLEFIELD, playerA, "Bloodchief Ascension"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain",3); + addCard(Zone.HAND, playerA, "Fireball"); + addCard(Zone.BATTLEFIELD, playerD, "Mountain",3); + addCard(Zone.HAND, playerD, "Fireball"); + addCard(Zone.BATTLEFIELD, playerC, "Mountain",3); + addCard(Zone.HAND, playerC, "Fireball"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain",21); + addCard(Zone.HAND, playerB, "Fireball"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fireball", playerA); + setChoice(playerA, "X=2"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Fireball", playerD); + setChoice(playerD, "X=2"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Fireball", playerC); + setChoice(playerC, "X=2"); + + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Fireball", playerB); + setChoice(playerB, "X=20"); + + // Player order: A -> D -> C -> B + + setStopAt(4, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 18); + assertLife(playerB, 0); + assertLife(playerC, 18); + assertLife(playerD, 18); + + Assert.assertTrue("playerB has lost", playerB.hasLost()); + + assertGraveyardCount(playerA, "Fireball", 1); + assertGraveyardCount(playerC, "Fireball", 1); + assertGraveyardCount(playerD, "Fireball", 1); + + assertCounterCount("Bloodchief Ascension", CounterType.QUEST, 2); // 1 opponent out of range + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PrimordialTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PrimordialTest.java index 6cd03426854..fec0313fc09 100644 --- a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PrimordialTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PrimordialTest.java @@ -29,7 +29,6 @@ package org.mage.test.multiplayer; import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestMultiPlayerBase; @@ -65,6 +64,7 @@ public class PrimordialTest extends CardTestMultiPlayerBase { assertPermanentCount(playerA, "Walking Corpse", 0); assertPermanentCount(playerA, "Pillarfield Ox", 1); assertGraveyardCount(playerC, "Walking Corpse", 1); + assertGraveyardCount(playerD, "Pillarfield Ox", 0); } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java index 435c57719db..148c3c34979 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java @@ -102,16 +102,18 @@ public class RandomPlayer extends ComputerPlayer { List playables = getPlayableAbilities(game); Ability ability; while (true) { - if (playables.size() == 1) + if (playables.size() == 1) { ability = playables.get(0); - else + } else { ability = playables.get(rnd.nextInt(playables.size())); + } List options = getPlayableOptions(ability, game); if (!options.isEmpty()) { - if (options.size() == 1) + if (options.size() == 1) { ability = options.get(0); - else + } else { ability = options.get(rnd.nextInt(options.size())); + } } if (ability.getManaCosts().getVariableCosts().size() > 0) { int amount = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost(); @@ -154,10 +156,11 @@ public class RandomPlayer extends ComputerPlayer { ability = source; } else { - if (options.size() == 1) + if (options.size() == 1) { ability = options.get(0); - else + } else { ability = options.get(rnd.nextInt(options.size())); + } } if (ability.isUsesStack()) { game.getStack().push(new StackAbility(ability, playerId)); @@ -203,15 +206,18 @@ public class RandomPlayer extends ComputerPlayer { @Override public void selectBlockers(Game game, UUID defendingPlayerId) { int numGroups = game.getCombat().getGroups().size(); - if (numGroups == 0) return; + if (numGroups == 0) { + return; + } List blockers = getAvailableBlockers(game); for (Permanent blocker: blockers) { int check = rnd.nextInt(numGroups + 1); if (check < numGroups) { CombatGroup group = game.getCombat().getGroups().get(check); - if (group.getAttackers().size() > 0) + if (group.getAttackers().size() > 0) { this.declareBlocker(this.getId(), blocker.getId(), group.getAttackers().get(0), game); + } } } actionCount++; @@ -243,8 +249,9 @@ public class RandomPlayer extends ComputerPlayer { protected boolean chooseRandomTarget(Target target, Ability source, Game game) { Set possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), playerId, game); - if (possibleTargets.isEmpty()) + if (possibleTargets.isEmpty()) { return false; + } if (!target.isRequired(source)) { if (rnd.nextInt(possibleTargets.size() + 1) == 0) { return false; @@ -300,8 +307,9 @@ public class RandomPlayer extends ComputerPlayer { @Override public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) { - if (cards.isEmpty()) + if (cards.isEmpty()) { return !target.isRequired(source); + } Card card = cards.getRandom(game); target.addTarget(card.getId(), source, game); return true; @@ -373,8 +381,9 @@ public class RandomPlayer extends ComputerPlayer { public Mode chooseMode(Modes modes, Ability source, Game game) { Iterator it = modes.values().iterator(); Mode mode = it.next(); - if (modes.size() == 1) + if (modes.size() == 1) { return mode; + } int modeNum = rnd.nextInt(modes.values().size()); for (int i = 0; i < modeNum; i++) { mode = it.next(); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index bf48c2f8738..4f99f385689 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -1,42 +1,65 @@ /* -* 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. -*/ - + * 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 org.mage.test.player; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import mage.MageObject; -import mage.abilities.*; +import mage.abilities.Abilities; +import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; +import mage.abilities.Mode; +import mage.abilities.Modes; +import mage.abilities.SpellAbility; +import mage.abilities.TriggeredAbility; +import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.VariableCost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.mana.ManaAbility; +import mage.abilities.mana.ManaOptions; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.decks.Deck; import mage.choices.Choice; +import mage.constants.ManaType; import mage.constants.Outcome; import mage.constants.PhaseStep; import mage.constants.RangeOfInfluence; import mage.constants.SpellAbilityType; +import mage.constants.Zone; import mage.counters.Counter; +import mage.counters.Counters; import mage.filter.FilterPermanent; import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterCreatureForCombat; @@ -47,59 +70,67 @@ import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.filter.predicate.permanent.BlockingPredicate; import mage.game.Game; +import mage.game.Graveyard; +import mage.game.Table; +import mage.game.combat.CombatGroup; +import mage.game.draft.Draft; +import mage.game.match.Match; +import mage.game.match.MatchPlayer; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; +import mage.game.tournament.Tournament; import mage.player.ai.ComputerPlayer; +import mage.players.Library; +import mage.players.ManaPool; import mage.players.Player; +import mage.players.net.UserData; import mage.target.Target; +import mage.target.TargetAmount; +import mage.target.TargetCard; import mage.target.TargetPermanent; import mage.target.TargetPlayer; -import mage.target.common.TargetCreaturePermanentAmount; -import org.junit.Ignore; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import mage.abilities.mana.ManaAbility; -import mage.abilities.mana.ManaOptions; -import mage.cards.Card; -import mage.constants.Zone; import mage.target.TargetSource; import mage.target.TargetSpell; import mage.target.common.TargetCardInHand; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCreaturePermanentAmount; import mage.target.common.TargetPermanentOrPlayer; +import org.junit.Ignore; + + /** * * @author BetaSteward_at_googlemail.com */ @Ignore -public class TestPlayer extends ComputerPlayer { +public class TestPlayer implements Player { + + private int maxCallsWithoutAction = 100; + private int foundNoAction = 0; + private boolean AIPlayer; private final List actions = new ArrayList<>(); private final List choices = new ArrayList<>(); private final List targets = new ArrayList<>(); private final List modesSet = new ArrayList<>(); - public TestPlayer(String name, RangeOfInfluence range) { - super(name, range); - human = false; + private final ComputerPlayer computerPlayer; + + + public TestPlayer(ComputerPlayer computerPlayer) { + this.computerPlayer = computerPlayer; + AIPlayer = false; } - public TestPlayer(final TestPlayer player) { - super(player); - } + public TestPlayer(final TestPlayer testPlayer) { + this.actions.addAll(testPlayer.actions); + this.choices.addAll(testPlayer.choices); + this.targets.addAll(testPlayer.targets); + this.modesSet.addAll(testPlayer.modesSet); + this.computerPlayer = testPlayer.computerPlayer.copy(); + this.foundNoAction = testPlayer.foundNoAction; - public void addAction(int turnNum, PhaseStep step, String action) { - actions.add(new PlayerAction(turnNum, step, action)); - } - - @Override - public int getActionCount() { - return actions.size(); } public void addChoice(String choice) { @@ -114,6 +145,205 @@ public class TestPlayer extends ComputerPlayer { targets.add(target); } + public ManaOptions getAvailableManaTest(Game game) { + return computerPlayer.getManaAvailable(game); + } + + public void addAction(int turnNum, PhaseStep step, String action) { + actions.add(new PlayerAction(turnNum, step, action)); + } + + /** + * + * @param maxCallsWithoutAction max number of priority passes a player may have for this test (default = 100) + */ + public void setMaxCallsWithoutAction(int maxCallsWithoutAction) { + this.maxCallsWithoutAction = maxCallsWithoutAction; + } + + protected Permanent findPermanent(FilterPermanent filter, UUID controllerId, Game game) { + List permanents = game.getBattlefield().getAllActivePermanents(filter, controllerId, game); + if (permanents.size() > 0) { + return permanents.get(0); + } + return null; + } + + private boolean checkExecuteCondition(String[] groups, Game game) { + if (groups[2].startsWith("spellOnStack=")) { + String spellOnStack = groups[2].substring(13); + for (StackObject stackObject : game.getStack()) { + if (stackObject.getStackAbility().toString().contains(spellOnStack)) { + return true; + } + } + return false; + } else if (groups[2].startsWith("!spellOnStack=")) { + String spellNotOnStack = groups[2].substring(14); + for (StackObject stackObject : game.getStack()) { + if (stackObject.getStackAbility().toString().contains(spellNotOnStack)) { + return false; + } + } + return true; + } else if (groups[2].startsWith("spellOnTopOfStack=")) { + String spellOnTopOFStack = groups[2].substring(18); + if (game.getStack().size() > 0) { + StackObject stackObject = game.getStack().getFirst(); + if (stackObject != null && stackObject.getStackAbility().toString().contains(spellOnTopOFStack)) { + return true; + } + } + return false; + } else if (groups[2].startsWith("manaInPool=")) { + String manaInPool = groups[2].substring(11); + int amountOfMana = Integer.parseInt(manaInPool); + return computerPlayer.getManaPool().getMana().count() >= amountOfMana; + } + return true; + } + +// private boolean checkSpellOnTopOfStackCondition(String[] groups, Game game) { +// if (groups.length > 2 && groups[2].startsWith("spellOnTopOfStack=")) { +// String spellOnTopOFStack = groups[2].substring(18); +// if (game.getStack().size() > 0) { +// StackObject stackObject = game.getStack().getFirst(); +// if (stackObject != null && stackObject.getStackAbility().toString().contains(spellOnTopOFStack)) { +// return true; +// } +// } +// return false; +// } +// return true; +// } + private boolean addTargets(Ability ability, String[] groups, Game game) { + boolean result = true; + for (int i = 1; i < groups.length; i++) { + String group = groups[i]; + if (group.startsWith("spellOnStack") || group.startsWith("spellOnTopOfStack") || group.startsWith("!spellOnStack") || group.startsWith("target=null") || group.startsWith("manaInPool=")) { + break; + } + if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) { + if (group.contains("FuseLeft-")) { + result = handleTargetString(group.substring(group.indexOf("FuseLeft-") + 9), ability, game); + } else if (group.startsWith("FuseRight-")) { + result = handleTargetString(group.substring(group.indexOf("FuseRight-") + 10), ability, game); + } else { + result = false; + } + } else { + result = handleTargetString(group, ability, game); + } + } + return result; + } + + private boolean handleTargetString(String target, Ability ability, Game game) { + boolean result = false; + if (target.startsWith("targetPlayer=")) { + result = handlePlayerTarget(target.substring(target.indexOf("targetPlayer=") + 13), ability, game); + } else if (target.startsWith("target=")) { + result = handleNonPlayerTargetTarget(target.substring(target.indexOf("target=") + 7), ability, game); + } + return result; + } + + private boolean handlePlayerTarget(String target, Ability ability, Game game) { + boolean result = true; + int targetsSet = 0; + for (Player player : game.getPlayers().values()) { + if (player.getName().equals(target)) { + ability.getTargets().get(0).addTarget(player.getId(), ability, game); + targetsSet++; + break; + } + } + if (targetsSet < 1) { + result = false; + } + return result; + } + + private boolean handleNonPlayerTargetTarget(String target, Ability ability, Game game) { + boolean result = true; + if (target == null) { + return true; // needed if spell has no target but waits until spell is on the stack + } + String[] targetList = target.split("\\^"); + int index = 0; + int targetsSet = 0; + for (String targetName : targetList) { + if (targetName.startsWith("mode=")) { + int modeNr = Integer.parseInt(targetName.substring(5, 6)); + if (modeNr == 0 || modeNr > ability.getModes().size()) { + throw new UnsupportedOperationException("Given mode number (" + modeNr + ") not available for " + ability.toString()); + } + int modeCounter = 1; + for (Mode mode : ability.getModes().values()) { + if (modeCounter == modeNr) { + ability.getModes().setMode(mode); + index = 0; // reset target index if mode changes + break; + } + modeCounter++; + } + targetName = targetName.substring(6); + } + if (ability.getTargets().size() == 0) { + throw new AssertionError("Ability has no targets. " + ability.toString()); + } + if (index >= ability.getTargets().size()) { + break; // this can happen if targets should be set but can't be used because of hexproof e.g. + } + Target currentTarget = ability.getTargets().get(index); + if (targetName.startsWith("targetPlayer=")) { + target = targetName.substring(targetName.indexOf("targetPlayer=") + 13); + for (Player player : game.getPlayers().values()) { + if (player.getName().equals(target)) { + currentTarget.addTarget(player.getId(), ability, game); + index++; + targetsSet++; + break; + } + } + } else { + for (UUID id : currentTarget.possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) { + MageObject object = game.getObject(id); + if (object != null + && ((!targetName.isEmpty() && object.getName().startsWith(targetName)) || (targetName.isEmpty() && object.getName().isEmpty()))) { + if (currentTarget.getNumberOfTargets() == 1) { + currentTarget.clearChosen(); + } + if (currentTarget instanceof TargetCreaturePermanentAmount) { + // supports only to set the complete amount to one target + TargetCreaturePermanentAmount targetAmount = (TargetCreaturePermanentAmount) currentTarget; + targetAmount.setAmount(ability, game); + int amount = targetAmount.getAmountRemaining(); + targetAmount.addTarget(id, amount, ability, game); + targetsSet++; + } else { + currentTarget.addTarget(id, ability, game); + targetsSet++; + } + if (currentTarget.getTargets().size() == currentTarget.getMaxNumberOfTargets()) { + index++; + } + break; + } + } + } + } + if (targetsSet != targetList.length) { + result = false; + } + return result; + } + + @Override + public int getActionCount() { + return actions.size(); + } + @Override public TestPlayer copy() { return new TestPlayer(this); @@ -121,7 +351,8 @@ public class TestPlayer extends ComputerPlayer { @Override public boolean priority(Game game) { - for (PlayerAction action: actions) { + int numberOfActions = actions.size(); + for (PlayerAction action : actions) { if (action.getTurnNum() == game.getTurnNum() && action.getStep() == game.getStep().getType()) { if (action.getAction().startsWith("activate:")) { @@ -131,7 +362,7 @@ public class TestPlayer extends ComputerPlayer { if (groups.length > 2 && !checkExecuteCondition(groups, game)) { break; } - for (Ability ability: this.getPlayable(game, true)) { + for (Ability ability : computerPlayer.getPlayable(game, true)) { if (ability.toString().startsWith(groups[0])) { Ability newAbility = ability.copy(); if (groups.length > 1 && !groups[1].equals("target=NO_TARGET")) { @@ -140,42 +371,38 @@ public class TestPlayer extends ComputerPlayer { break; } } - this.activateAbility((ActivatedAbility)newAbility, game); + computerPlayer.activateAbility((ActivatedAbility) newAbility, game); actions.remove(action); return true; } - } - } else - - if (action.getAction().startsWith("manaActivate:")) { + } + } else if (action.getAction().startsWith("manaActivate:")) { String command = action.getAction(); command = command.substring(command.indexOf("manaActivate:") + 13); String[] groups = command.split("\\$"); - List manaPerms = this.getAvailableManaProducers(game); - for (Permanent perm: manaPerms) { - for (Ability manaAbility: perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { - if (manaAbility.toString().startsWith(groups[0])) { - Ability newManaAbility = manaAbility.copy(); - this.activateAbility((ActivatedAbility)newManaAbility, game); + List manaPerms = computerPlayer.getAvailableManaProducers(game); + for (Permanent perm : manaPerms) { + for (Ability manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { + if (manaAbility.toString().startsWith(groups[0])) { + Ability newManaAbility = manaAbility.copy(); + computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); actions.remove(action); return true; - } - } + } + } } - List manaPermsWithCost = this.getAvailableManaProducersWithCost(game); - for (Permanent perm: manaPermsWithCost) { - for (ManaAbility manaAbility: perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { - if (manaAbility.toString().startsWith(groups[0]) && manaAbility.canActivate(playerId, game)) { - Ability newManaAbility = manaAbility.copy(); - this.activateAbility((ActivatedAbility)newManaAbility, game); + List manaPermsWithCost = computerPlayer.getAvailableManaProducersWithCost(game); + for (Permanent perm : manaPermsWithCost) { + for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { + if (manaAbility.toString().startsWith(groups[0]) && manaAbility.canActivate(computerPlayer.getId(), game)) { + Ability newManaAbility = manaAbility.copy(); + computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); actions.remove(action); return true; - } - } + } + } } - } else - - if (action.getAction().startsWith("addCounters:")) { + } else if (action.getAction().startsWith("addCounters:")) { String command = action.getAction(); command = command.substring(command.indexOf("addCounters:") + 12); String[] groups = command.split("\\$"); @@ -189,21 +416,29 @@ public class TestPlayer extends ComputerPlayer { } } } - pass(game); + if (AIPlayer) { + computerPlayer.priority(game); + } else { + computerPlayer.pass(game); + } + // check to prevent endless loops + if (numberOfActions == actions.size()) { + foundNoAction++; + if (foundNoAction > maxCallsWithoutAction) { + throw new AssertionError("More priority calls to " +getName() + " and doing no action than allowed (" + maxCallsWithoutAction +")"); + } + } else { + foundNoAction = 0; + } return false; } @Override public void selectAttackers(Game game, UUID attackingPlayerId) { UUID defenderId = null; - for (PlayerAction action: actions) { + for (PlayerAction action : actions) { if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) { - for (UUID uuid: game.getCombat().getDefenders()) { - Player defender = game.getPlayer(uuid); - if (defender != null) { - defenderId = uuid; - } - } + String command = action.getAction(); command = command.substring(command.indexOf("attack:") + 7); String[] groups = command.split("\\$"); @@ -211,19 +446,36 @@ public class TestPlayer extends ComputerPlayer { String group = groups[i]; if (group.startsWith("planeswalker=")) { String planeswalkerName = group.substring(group.indexOf("planeswalker=") + 13); - for (Permanent permanent :game.getBattlefield().getAllActivePermanents(new FilterPlaneswalkerPermanent(), game)) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterPlaneswalkerPermanent(), game)) { if (permanent.getName().equals(planeswalkerName)) { - defenderId = permanent.getId(); + defenderId = permanent.getId(); } } } + if (group.startsWith("defendingPlayer=")) { + String defendingPlayerName = group.substring(group.indexOf("defendingPlayer=") + 16); + for (Player defendingPlayer : game.getPlayers().values()) { + if (defendingPlayer.getName().equals(defendingPlayerName)) { + defenderId = defendingPlayer.getId(); + break; + } + } + } + } + if (defenderId == null) { + for (UUID uuid : game.getCombat().getDefenders()) { + Player defender = game.getPlayer(uuid); + if (defender != null) { + defenderId = uuid; + } + } } FilterCreatureForCombat filter = new FilterCreatureForCombat(); filter.add(new NamePredicate(groups[0])); filter.add(Predicates.not(new AttackingPredicate())); - Permanent attacker = findPermanent(filter, playerId, game); + Permanent attacker = findPermanent(filter, computerPlayer.getId(), game); if (attacker != null && attacker.canAttack(defenderId, game)) { - this.declareAttacker(attacker.getId(), defenderId, game, false); + computerPlayer.declareAttacker(attacker.getId(), defenderId, game, false); } } } @@ -231,8 +483,8 @@ public class TestPlayer extends ComputerPlayer { @Override public void selectBlockers(Game game, UUID defendingPlayerId) { - UUID opponentId = game.getOpponents(playerId).iterator().next(); - for (PlayerAction action: actions) { + UUID opponentId = game.getOpponents(computerPlayer.getId()).iterator().next(); + for (PlayerAction action : actions) { if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("block:")) { String command = action.getAction(); command = command.substring(command.indexOf("block:") + 6); @@ -240,13 +492,13 @@ public class TestPlayer extends ComputerPlayer { FilterCreatureForCombatBlock filterBlocker = new FilterCreatureForCombatBlock(); filterBlocker.add(new NamePredicate(groups[0])); filterBlocker.add(Predicates.not(new BlockingPredicate())); - Permanent blocker = findPermanent(filterBlocker, playerId, game); + Permanent blocker = findPermanent(filterBlocker, computerPlayer.getId(), game); if (blocker != null) { FilterAttackingCreature filterAttacker = new FilterAttackingCreature(); filterAttacker.add(new NamePredicate(groups[1])); Permanent attacker = findPermanent(filterAttacker, opponentId, game); if (attacker != null) { - this.declareBlocker(defendingPlayerId, blocker.getId(), attacker.getId(), game); + computerPlayer.declareBlocker(defendingPlayerId, blocker.getId(), attacker.getId(), game); } } } @@ -256,9 +508,9 @@ public class TestPlayer extends ComputerPlayer { @Override public Mode chooseMode(Modes modes, Ability source, Game game) { if (!modesSet.isEmpty() && modes.getMaxModes() > modes.getSelectedModes().size()) { - int selectedMode = Integer.parseInt(modesSet.get(0)); + int selectedMode = Integer.parseInt(modesSet.get(0)); int i = 1; - for (Mode mode: modes.values()) { + for (Mode mode : modes.values()) { if (i == selectedMode) { modesSet.remove(0); return mode; @@ -266,14 +518,14 @@ public class TestPlayer extends ComputerPlayer { i++; } } - return super.chooseMode(modes, source, game); //To change body of generated methods, choose Tools | Templates. + return computerPlayer.chooseMode(modes, source, game); //To change body of generated methods, choose Tools | Templates. } @Override public boolean choose(Outcome outcome, Choice choice, Game game) { if (!choices.isEmpty()) { - for (String choose2: choices) { - for (String choose1: choice.getChoices()) { + for (String choose2 : choices) { + for (String choose1 : choice.getChoices()) { if (choose1.equals(choose2)) { choice.setChoice(choose2); choices.remove(choose2); @@ -282,13 +534,13 @@ public class TestPlayer extends ComputerPlayer { } } } - return super.choose(outcome, choice, game); + return computerPlayer.choose(outcome, choice, game); } @Override public int chooseReplacementEffect(Map rEffects, Game game) { if (!choices.isEmpty()) { - for (String choice: choices) { + for (String choice : choices) { for (int index = 0; index < rEffects.size(); index++) { if (choice.equals(rEffects.get(index))) { choices.remove(choice); @@ -297,7 +549,7 @@ public class TestPlayer extends ComputerPlayer { } } } - return super.chooseReplacementEffect(rEffects, game); + return computerPlayer.chooseReplacementEffect(rEffects, game); } @Override @@ -310,22 +562,22 @@ public class TestPlayer extends ComputerPlayer { } else { filterPermanent = ((TargetPermanent) target).getFilter(); } - for (String choose2: choices) { + for (String choose2 : choices) { String[] targetList = choose2.split("\\^"); boolean targetFound = false; - for (String targetName: targetList) { + for (String targetName : targetList) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterPermanent, game)) { if (target.getTargets().contains(permanent.getId())) { continue; } if (permanent.getName().equals(targetName)) { - if (target.isNotTarget() || ((TargetPermanent)target).canTarget(playerId, permanent.getId(), null, game)) { + if (target.isNotTarget() || ((TargetPermanent) target).canTarget(computerPlayer.getId(), permanent.getId(), null, game)) { target.add(permanent.getId(), game); targetFound = true; break; } - } else if ((permanent.getName()+"-"+permanent.getExpansionSetCode()).equals(targetName)) { - if (target.isNotTarget() || ((TargetPermanent)target).canTarget(playerId, permanent.getId(), null, game)) { + } else if ((permanent.getName() + "-" + permanent.getExpansionSetCode()).equals(targetName)) { + if (target.isNotTarget() || ((TargetPermanent) target).canTarget(computerPlayer.getId(), permanent.getId(), null, game)) { target.add(permanent.getId(), game); targetFound = true; break; @@ -340,13 +592,13 @@ public class TestPlayer extends ComputerPlayer { } } if (target instanceof TargetPlayer) { - for (Player player :game.getPlayers().values()) { - for (String choose2: choices) { + for (Player player : game.getPlayers().values()) { + for (String choose2 : choices) { if (player.getName().equals(choose2)) { - if (((TargetPlayer)target).canTarget(playerId, player.getId(), null, game) && !target.getTargets().contains(player.getId())) { + if (((TargetPlayer) target).canTarget(computerPlayer.getId(), player.getId(), null, game) && !target.getTargets().contains(player.getId())) { target.add(player.getId(), game); choices.remove(choose2); - return true; + return true; } } } @@ -355,14 +607,14 @@ public class TestPlayer extends ComputerPlayer { if (target instanceof TargetSource) { Set possibleTargets; TargetSource t = ((TargetSource) target); - possibleTargets = t.possibleTargets(sourceId, playerId, game); + possibleTargets = t.possibleTargets(sourceId, computerPlayer.getId(), game); for (UUID targetId : possibleTargets) { MageObject targetObject = game.getObject(targetId); if (targetObject != null) { - for (String choose2: choices) { + for (String choose2 : choices) { String[] targetList = choose2.split("\\^"); boolean targetFound = false; - for (String targetName: targetList) { + for (String targetName : targetList) { if (targetObject.getName().equals(targetName)) { List alreadyTargetted = target.getTargets(); if (t.canTarget(targetObject.getId(), game)) { @@ -377,39 +629,43 @@ public class TestPlayer extends ComputerPlayer { if (targetFound) { choices.remove(choose2); return true; - } + } } } } } } - return super.choose(outcome, target, sourceId, game, options); + return computerPlayer.choose(outcome, target, sourceId, game, options); } @Override public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) { if (!targets.isEmpty()) { + UUID abilityControllerId = computerPlayer.getId(); + if (target.getTargetController() != null && target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); + } if ((target instanceof TargetPermanent) || (target instanceof TargetPermanentOrPlayer)) { - for (String targetDefinition: targets) { + for (String targetDefinition : targets) { String[] targetList = targetDefinition.split("\\^"); boolean targetFound = false; - for (String targetName: targetList) { + for (String targetName : targetList) { boolean originOnly = false; boolean copyOnly = false; if (targetName.endsWith("]")) { if (targetName.endsWith("[no copy]")) { originOnly = true; - targetName = targetName.substring(0, targetName.length()-9); + targetName = targetName.substring(0, targetName.length() - 9); } if (targetName.endsWith("[only copy]")) { copyOnly = true; - targetName = targetName.substring(0, targetName.length()-11); + targetName = targetName.substring(0, targetName.length() - 11); } } - for (Permanent permanent : game.getBattlefield().getAllActivePermanents((FilterPermanent)target.getFilter(), game)) { - if (permanent.getName().equals(targetName) || (permanent.getName()+"-"+permanent.getExpansionSetCode()).equals(targetName)) { - if (((TargetPermanent)target).canTarget(source == null ? this.getId(): source.getControllerId(), permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) { - if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly )) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents((FilterPermanent) target.getFilter(), game)) { + if (permanent.getName().equals(targetName) || (permanent.getName() + "-" + permanent.getExpansionSetCode()).equals(targetName)) { + if (((TargetPermanent) target).canTarget(abilityControllerId, permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) { + if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { target.add(permanent.getId(), game); targetFound = true; break; @@ -425,28 +681,28 @@ public class TestPlayer extends ComputerPlayer { } } if (target instanceof TargetPlayer) { - for (String targetDefinition: targets) { + for (String targetDefinition : targets) { if (targetDefinition.startsWith("targetPlayer=")) { String playerName = targetDefinition.substring(targetDefinition.indexOf("targetPlayer=") + 13); - for (Player player: game.getPlayers().values()) { + for (Player player : game.getPlayers().values()) { if (player.getName().equals(playerName) - && ((TargetPlayer) target).canTarget(playerId, player.getId(), source, game)) { + && ((TargetPlayer) target).canTarget(computerPlayer.getId(), player.getId(), source, game)) { target.add(player.getId(), game); return true; } } } } - + } if (target instanceof TargetCardInHand) { - for (String targetDefinition: targets) { + for (String targetDefinition : targets) { String[] targetList = targetDefinition.split("\\^"); boolean targetFound = false; - for (String targetName: targetList) { - for (Card card: this.getHand().getCards(((TargetCardInHand)target).getFilter(), game)) { - if (card.getName().equals(targetName) || (card.getName()+"-"+card.getExpansionSetCode()).equals(targetName)) { - if (((TargetCardInHand)target).canTarget(source.getControllerId(), card.getId(), source, game) && !target.getTargets().contains(card.getId())) { + for (String targetName : targetList) { + for (Card card : computerPlayer.getHand().getCards(((TargetCardInHand) target).getFilter(), game)) { + if (card.getName().equals(targetName) || (card.getName() + "-" + card.getExpansionSetCode()).equals(targetName)) { + if (((TargetCardInHand) target).canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) { target.add(card.getId(), game); targetFound = true; break; @@ -462,41 +718,40 @@ public class TestPlayer extends ComputerPlayer { } if (target instanceof TargetSpell) { - for (String targetDefinition: targets) { + for (String targetDefinition : targets) { String[] targetList = targetDefinition.split("\\^"); boolean targetFound = false; - for (String targetName: targetList) { - for(StackObject stackObject: game.getStack()) { + for (String targetName : targetList) { + for (StackObject stackObject : game.getStack()) { if (stackObject.getName().equals(targetName)) { target.add(stackObject.getId(), game); targetFound = true; - break; - } + break; + } } - } + } if (targetFound) { targets.remove(targetDefinition); return true; - } - } + } + } } - } - return super.chooseTarget(outcome, target, source, game); + return computerPlayer.chooseTarget(outcome, target, source, game); } @Override public TriggeredAbility chooseTriggeredAbility(List abilities, Game game) { if (!choices.isEmpty()) { - for(TriggeredAbility ability :abilities) { + for (TriggeredAbility ability : abilities) { if (ability.toString().startsWith(choices.get(0))) { choices.remove(0); return ability; } } } - return super.chooseTriggeredAbility(abilities, game); + return computerPlayer.chooseTriggeredAbility(abilities, game); } @Override @@ -523,7 +778,7 @@ public class TestPlayer extends ComputerPlayer { return xValue; } } - return super.announceXMana(min, max, message, game, ability); + return computerPlayer.announceXMana(min, max, message, game, ability); } @Override @@ -535,7 +790,7 @@ public class TestPlayer extends ComputerPlayer { return xValue; } } - return super.announceXCost(min, max, message, game, ability, null); + return computerPlayer.announceXCost(min, max, message, game, ability, null); } @Override @@ -547,171 +802,1025 @@ public class TestPlayer extends ComputerPlayer { return xValue; } } - return super.getAmount(min, max, message, game); + return computerPlayer.getAmount(min, max, message, game); } - protected Permanent findPermanent(FilterPermanent filter, UUID controllerId, Game game) { - List permanents = game.getBattlefield().getAllActivePermanents(filter, controllerId, game); - if (permanents.size() > 0) { - return permanents.get(0); - } - return null; + @Override + public void addAbility(Ability ability) { + computerPlayer.addAbility(ability); } - private boolean checkExecuteCondition(String[] groups, Game game) { - if (groups[2].startsWith("spellOnStack=")) { - String spellOnStack = groups[2].substring(13); - for (StackObject stackObject: game.getStack()) { - if (stackObject.getStackAbility().toString().contains(spellOnStack)) { - return true; - } - } - return false; - } else if (groups[2].startsWith("!spellOnStack=")) { - String spellNotOnStack = groups[2].substring(14); - for (StackObject stackObject: game.getStack()) { - if (stackObject.getStackAbility().toString().contains(spellNotOnStack)) { - return false; - } - } - return true; - } else if (groups[2].startsWith("spellOnTopOfStack=")) { - String spellOnTopOFStack = groups[2].substring(18); - if (game.getStack().size() > 0) { - StackObject stackObject = game.getStack().getFirst(); - if (stackObject != null && stackObject.getStackAbility().toString().contains(spellOnTopOFStack)) { - return true; - } - } - return false; - } else if (groups[2].startsWith("manaInPool=")) { - String manaInPool = groups[2].substring(11); - int amountOfMana = Integer.parseInt(manaInPool); - return this.getManaPool().getMana().count() >= amountOfMana; - } - return true; + @Override + public boolean activateAbility(ActivatedAbility ability, Game game) { + return computerPlayer.activateAbility(ability, game); } -// private boolean checkSpellOnTopOfStackCondition(String[] groups, Game game) { -// if (groups.length > 2 && groups[2].startsWith("spellOnTopOfStack=")) { -// String spellOnTopOFStack = groups[2].substring(18); -// if (game.getStack().size() > 0) { -// StackObject stackObject = game.getStack().getFirst(); -// if (stackObject != null && stackObject.getStackAbility().toString().contains(spellOnTopOFStack)) { -// return true; -// } -// } -// return false; -// } -// return true; -// } - - private boolean addTargets(Ability ability, String[] groups, Game game) { - boolean result = true; - for (int i = 1; i < groups.length; i++) { - String group = groups[i]; - if (group.startsWith("spellOnStack") || group.startsWith("spellOnTopOfStack") || group.startsWith("!spellOnStack") || group.startsWith("target=null") || group.startsWith("manaInPool=")) { - break; - } - if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) { - if (group.contains("FuseLeft-")) { - result = handleTargetString(group.substring(group.indexOf("FuseLeft-") + 9), ability, game); - } else if(group.startsWith("FuseRight-")) { - result = handleTargetString(group.substring(group.indexOf("FuseRight-") + 10), ability, game); - } else { - result = false; - } - } else { - result = handleTargetString(group, ability, game); - } - } - return result; + @Override + public void abort() { + computerPlayer.abort(); } - private boolean handleTargetString(String target, Ability ability, Game game){ - boolean result = false; - if (target.startsWith("targetPlayer=")) { - result = handlePlayerTarget(target.substring(target.indexOf("targetPlayer=") + 13), ability, game); - } else if (target.startsWith("target=")) { - result = handleNonPlayerTargetTarget(target.substring(target.indexOf("target=") + 7), ability, game); - } - return result; - } - - private boolean handlePlayerTarget(String target, Ability ability, Game game) { - boolean result = true; - int targetsSet = 0; - for (Player player: game.getPlayers().values()) { - if (player.getName().equals(target)) { - ability.getTargets().get(0).addTarget(player.getId(), ability, game); - targetsSet++; - break; - } - } - if (targetsSet < 1) { - result = false; - } - return result; - } - - private boolean handleNonPlayerTargetTarget(String target, Ability ability, Game game) { - boolean result = true; - if (target == null) { - return true; // needed if spell has no target but waits until spell is on the stack - } - String[] targetList = target.split("\\^"); - int index = 0; - int targetsSet = 0; - for (String targetName: targetList) { - if (targetName.startsWith("targetPlayer=")) { - target = targetName.substring(targetName.indexOf("targetPlayer=") + 13); - for (Player player: game.getPlayers().values()) { - if (player.getName().equals(target)) { - ability.getTargets().get(index).addTarget(player.getId(), ability, game); - index++; - targetsSet++; - break; - } - } - } else { - if (ability.getTargets().size() == 0) { - throw new AssertionError("Ability has no targets. " + ability.toString()); - } - for (UUID id: ability.getTargets().get(0).possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) { - MageObject object = game.getObject(id); - if (object != null && - ((!targetName.isEmpty() && object.getName().startsWith(targetName)) || (targetName.isEmpty() && object.getName().isEmpty()))) { - if (index >= ability.getTargets().size()) { - index--; - } - if (ability.getTargets().get(index).getNumberOfTargets() == 1) { - ability.getTargets().get(index).clearChosen(); - } - if (ability.getTargets().get(index) instanceof TargetCreaturePermanentAmount) { - // supports only to set the complete amount to one target - TargetCreaturePermanentAmount targetAmount = (TargetCreaturePermanentAmount) ability.getTargets().get(index); - targetAmount.setAmount(ability, game); - int amount = targetAmount.getAmountRemaining(); - targetAmount.addTarget(id, amount,ability, game); - targetsSet++; - } else { - ability.getTargets().get(index).addTarget(id, ability, game); - targetsSet++; - } - index++; - break; - } - } - } - } - if (targetsSet != targetList.length) { - result = false; - } - return result; + @Override + public void abortReset() { + computerPlayer.abortReset(); } - public ManaOptions getAvailableManaTest(Game game) { - return getManaAvailable(game); + @Override + public void won(Game game) { + computerPlayer.won(game); } + + @Override + public void restore(Player player) { + computerPlayer.restore(player); + } + + @Override + public void useDeck(Deck deck, Game game) { + computerPlayer.useDeck(deck, game); + } + + @Override + public void init(Game game) { + computerPlayer.init(game); + } + + @Override + public void init(Game game, boolean testMode) { + computerPlayer.init(game, testMode); + } + + @Override + public void reset() { + computerPlayer.reset(); + } + + @Override + public Counters getCounters() { + return computerPlayer.getCounters(); + } + + @Override + public void otherPlayerLeftGame(Game game) { + computerPlayer.otherPlayerLeftGame(game); + } + + @Override + public void beginTurn(Game game) { + computerPlayer.beginTurn(game); + } + + @Override + public RangeOfInfluence getRange() { + return computerPlayer.getRange(); + } + + @Override + public Set getInRange() { + return computerPlayer.getInRange(); + } + + @Override + public Set getPlayersUnderYourControl() { + return computerPlayer.getPlayersUnderYourControl(); + } + + @Override + public void controlPlayersTurn(Game game, UUID playerId) { + computerPlayer.controlPlayersTurn(game, playerId); + } + + @Override + public void setTurnControlledBy(UUID playerId) { + computerPlayer.setTurnControlledBy(playerId); + } + + @Override + public UUID getTurnControlledBy() { + return computerPlayer.getTurnControlledBy(); + } + + @Override + public void resetOtherTurnsControlled() { + computerPlayer.resetOtherTurnsControlled(); + } + + @Override + public boolean isGameUnderControl() { + return computerPlayer.isGameUnderControl(); + } + + @Override + public void setGameUnderYourControl(boolean value) { + computerPlayer.setGameUnderYourControl(value); + } + + @Override + public void endOfTurn(Game game) { + computerPlayer.endOfTurn(game); + } + + @Override + public boolean canBeTargetedBy(MageObject source, UUID sourceControllerId, Game game) { + return computerPlayer.canBeTargetedBy(source, sourceControllerId, game); + } + + @Override + public boolean hasProtectionFrom(MageObject source, Game game) { + return computerPlayer.hasProtectionFrom(source, game); + } + + @Override + public int drawCards(int num, Game game) { + return computerPlayer.drawCards(num, game); + } + + @Override + public int drawCards(int num, Game game, ArrayList appliedEffects) { + return computerPlayer.drawCards(num, game, appliedEffects); + } + + @Override + public void discardToMax(Game game) { + computerPlayer.discardToMax(game); + } + + @Override + public boolean putInHand(Card card, Game game) { + return computerPlayer.putInHand(card, game); + } + + @Override + public boolean removeFromHand(Card card, Game game) { + return computerPlayer.removeFromHand(card, game); + } + + @Override + public boolean removeFromLibrary(Card card, Game game) { + return computerPlayer.removeFromLibrary(card, game); + } + + @Override + public void discard(int amount, Ability source, Game game) { + computerPlayer.discard(amount, source, game); + } + + @Override + public Card discardOne(boolean random, Ability source, Game game) { + return computerPlayer.discardOne(random, source, game); + } + + @Override + public Cards discard(int amount, boolean random, Ability source, Game game) { + return computerPlayer.discard(amount, random, source, game); + } + + @Override + public boolean discard(Card card, Ability source, Game game) { + return computerPlayer.discard(card, source, game); + } + + @Override + public List getAttachments() { + return computerPlayer.getAttachments(); + } + + @Override + public boolean addAttachment(UUID permanentId, Game game) { + return computerPlayer.addAttachment(permanentId, game); + } + + @Override + public boolean removeAttachment(Permanent attachment, Game game) { + return computerPlayer.removeAttachment(attachment, game); + } + + @Override + public boolean removeFromBattlefield(Permanent permanent, Game game) { + return computerPlayer.removeFromBattlefield(permanent, game); + } + + @Override + public boolean putInGraveyard(Card card, Game game, boolean fromBattlefield) { + return computerPlayer.putInGraveyard(card, game, fromBattlefield); + } + + @Override + public boolean removeFromGraveyard(Card card, Game game) { + return computerPlayer.removeFromGraveyard(card, game); + } + + @Override + public boolean putCardsOnBottomOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder) { + return computerPlayer.putCardsOnBottomOfLibrary(cards, game, source, anyOrder); + } + + @Override + public boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder) { + return computerPlayer.putCardsOnTopOfLibrary(cards, game, source, anyOrder); + } + + @Override + public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts manaCosts) { + computerPlayer.setCastSourceIdWithAlternateMana(sourceId, manaCosts); + } + + @Override + public UUID getCastSourceIdWithAlternateMana() { + return computerPlayer.getCastSourceIdWithAlternateMana(); + } + + @Override + public ManaCosts getCastSourceIdManaCosts() { + return computerPlayer.getCastSourceIdManaCosts(); + } + + @Override + public boolean isInPayManaMode() { + return computerPlayer.isInPayManaMode(); + } + + @Override + public boolean cast(SpellAbility ability, Game game, boolean noMana) { + return computerPlayer.cast(ability, game, noMana); + } + + @Override + public boolean playLand(Card card, Game game) { + return computerPlayer.playLand(card, game); + } + + @Override + public boolean triggerAbility(TriggeredAbility source, Game game) { + return computerPlayer.triggerAbility(source, game); + } + + @Override + public LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game) { + return computerPlayer.getUseableActivatedAbilities(object, zone, game); + } + + @Override + public int getLandsPlayed() { + return computerPlayer.getLandsPlayed(); + } + + @Override + public boolean canPlayLand() { + return computerPlayer.canPlayLand(); + } + + @Override + public void shuffleLibrary(Game game) { + computerPlayer.shuffleLibrary(game); + } + + @Override + public void revealCards(String name, Cards cards, Game game) { + computerPlayer.revealCards(name, cards, game); + } + + @Override + public void revealCards(String name, Cards cards, Game game, boolean postToLog) { + computerPlayer.revealCards(name, cards, game, postToLog); + } + + @Override + public void lookAtCards(String name, Cards cards, Game game) { + computerPlayer.lookAtCards(name, cards, game); + } + + @Override + public void phasing(Game game) { + computerPlayer.phasing(game); + } + + @Override + public void untap(Game game) { + computerPlayer.untap(game); + } + + @Override + public UUID getId() { + return computerPlayer.getId(); + } + + @Override + public Cards getHand() { + return computerPlayer.getHand(); + } + + @Override + public Graveyard getGraveyard() { + return computerPlayer.getGraveyard(); + } + + @Override + public ManaPool getManaPool() { + return computerPlayer.getManaPool(); + } + + @Override + public String getName() { + return computerPlayer.getName(); + } + + @Override + public String getLogName() { + return computerPlayer.getLogName(); + } + + @Override + public boolean isHuman() { + return computerPlayer.isHuman(); + } + + @Override + public Library getLibrary() { + return computerPlayer.getLibrary(); + } + + @Override + public Cards getSideboard() { + return computerPlayer.getSideboard(); + } + + @Override + public int getLife() { + return computerPlayer.getLife(); + } + + @Override + public void initLife(int life) { + computerPlayer.initLife(life); + } + + @Override + public void setLife(int life, Game game) { + computerPlayer.setLife(life, game); + } + + @Override + public void setLifeTotalCanChange(boolean lifeTotalCanChange) { + computerPlayer.setLifeTotalCanChange(lifeTotalCanChange); + } + + @Override + public boolean isLifeTotalCanChange() { + return computerPlayer.isLifeTotalCanChange(); + } + + @Override + public List getAlternativeSourceCosts() { + return computerPlayer.getAlternativeSourceCosts(); + } + + @Override + public boolean isCanLoseLife() { + return computerPlayer.isCanLoseLife(); + } + + @Override + public void setCanLoseLife(boolean canLoseLife) { + computerPlayer.setCanLoseLife(canLoseLife); + } + + @Override + public int loseLife(int amount, Game game) { + return computerPlayer.loseLife(amount, game); + } + + @Override + public boolean isCanGainLife() { + return computerPlayer.isCanGainLife(); + } + + @Override + public void setCanGainLife(boolean canGainLife) { + computerPlayer.setCanGainLife(canGainLife); + } + + @Override + public int gainLife(int amount, Game game) { + return computerPlayer.gainLife(amount, game); + } + + @Override + public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable) { + return computerPlayer.damage(damage, sourceId, game, combatDamage, preventable); + } + + @Override + public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, ArrayList appliedEffects) { + return computerPlayer.damage(damage, sourceId, game, combatDamage, preventable, appliedEffects); + } + + @Override + public void addCounters(Counter counter, Game game) { + computerPlayer.addCounters(counter, game); + } + + @Override + public Abilities getAbilities() { + return computerPlayer.getAbilities(); + } + + @Override + public int getLandsPerTurn() { + return computerPlayer.getLandsPerTurn(); + } + + @Override + public void setLandsPerTurn(int landsPerTurn) { + computerPlayer.setLandsPerTurn(landsPerTurn); + } + + @Override + public int getLoyaltyUsePerTurn() { + return computerPlayer.getLoyaltyUsePerTurn(); + } + + @Override + public void setLoyaltyUsePerTurn(int loyaltyUsePerTurn) { + computerPlayer.setLoyaltyUsePerTurn(loyaltyUsePerTurn); + } + + @Override + public int getMaxHandSize() { + return computerPlayer.getMaxHandSize(); + } + + @Override + public void setMaxHandSize(int maxHandSize) { + computerPlayer.setMaxHandSize(maxHandSize); + } + + @Override + public void setMaxAttackedBy(int maxAttackedBy) { + computerPlayer.setMaxAttackedBy(maxAttackedBy); + } + + @Override + public int getMaxAttackedBy() { + return computerPlayer.getMaxAttackedBy(); + } + + @Override + public void setResponseString(String responseString) { + computerPlayer.setResponseString(responseString); + } + + @Override + public void setResponseManaType(UUID manaTypePlayerId, ManaType responseManaType) { + computerPlayer.setResponseManaType(manaTypePlayerId, responseManaType); + } + + @Override + public void setResponseUUID(UUID responseUUID) { + computerPlayer.setResponseUUID(responseUUID); + } + + @Override + public void setResponseBoolean(Boolean responseBoolean) { + computerPlayer.setResponseBoolean(responseBoolean); + } + + @Override + public void setResponseInteger(Integer responseInteger) { + computerPlayer.setResponseInteger(responseInteger); + } + + @Override + public boolean isPassed() { + return computerPlayer.isPassed(); + } + + @Override + public void pass(Game game) { + computerPlayer.pass(game); + } + + @Override + public boolean isEmptyDraw() { + return computerPlayer.isEmptyDraw(); + } + + @Override + public void resetPassed() { + computerPlayer.resetPassed(); + } + + @Override + public void quit(Game game) { + computerPlayer.quit(game); + } + + @Override + public void timerTimeout(Game game) { + computerPlayer.timerTimeout(game); + } + + @Override + public void idleTimeout(Game game) { + computerPlayer.idleTimeout(game); + } + + @Override + public void concede(Game game) { + computerPlayer.concede(game); + } + + @Override + public void sendPlayerAction(mage.constants.PlayerAction playerAction, Game game) { + computerPlayer.sendPlayerAction(playerAction, game); + } + + @Override + public void leave() { + computerPlayer.leave(); + } + + @Override + public boolean hasLeft() { + return computerPlayer.hasLeft(); + } + + @Override + public void lost(Game game) { + computerPlayer.lost(game); + } + + @Override + public void lostForced(Game game) { + computerPlayer.lostForced(game); + } + + @Override + public boolean canLose(Game game) { + return computerPlayer.canLose(game); + } + + @Override + public boolean hasLost() { + return computerPlayer.hasLost(); + } + + @Override + public boolean isInGame() { + return computerPlayer.isInGame(); + } + + @Override + public boolean hasWon() { + return computerPlayer.hasWon(); + } + + @Override + public void declareAttacker(UUID attackerId, UUID defenderId, Game game, boolean allowUndo) { + computerPlayer.declareAttacker(attackerId, defenderId, game, allowUndo); + } + + @Override + public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game) { + computerPlayer.declareBlocker(defenderId, blockerId, attackerId, game); + } + + @Override + public boolean searchLibrary(TargetCardInLibrary target, Game game) { + return computerPlayer.searchLibrary(target, game); + } + + @Override + public boolean searchLibrary(TargetCardInLibrary target, Game game, UUID targetPlayerId) { + return computerPlayer.searchLibrary(target, game, targetPlayerId); + } + + @Override + public boolean flipCoin(Game game) { + return computerPlayer.flipCoin(game); + } + + @Override + public boolean flipCoin(Game game, ArrayList appliedEffects) { + return computerPlayer.flipCoin(game, appliedEffects); + } + + @Override + public List getAvailableAttackers(Game game) { + return computerPlayer.getAvailableAttackers(game); + } + + @Override + public List getAvailableAttackers(UUID defenderId, Game game) { + return computerPlayer.getAvailableAttackers(defenderId, game); + } + + @Override + public List getAvailableBlockers(Game game) { + return computerPlayer.getAvailableBlockers(game); + } + + @Override + public ManaOptions getManaAvailable(Game game) { + return computerPlayer.getManaAvailable(game); + } + + public List getAvailableManaProducersWithCost(Game game) { + return computerPlayer.getAvailableManaProducersWithCost(game); + } + + @Override + public List getPlayable(Game game, boolean hidden) { + return computerPlayer.getPlayable(game, hidden); + } + + @Override + public Set getPlayableInHand(Game game) { + return computerPlayer.getPlayableInHand(game); + } + + @Override + public List getPlayableOptions(Ability ability, Game game) { + return computerPlayer.getPlayableOptions(ability, game); + } + + @Override + public boolean isTestMode() { + return computerPlayer.isTestMode(); + } + + @Override + public void setTestMode(boolean value) { + computerPlayer.setTestMode(value); + } + + @Override + public boolean isTopCardRevealed() { + return computerPlayer.isTopCardRevealed(); + } + + @Override + public void setTopCardRevealed(boolean topCardRevealed) { + computerPlayer.setTopCardRevealed(topCardRevealed); + } + + @Override + public UserData getUserData() { + return computerPlayer.getUserData(); + } + + @Override + public void setUserData(UserData userData) { + computerPlayer.setUserData(userData); + } + + @Override + public void addAction(String action) { + computerPlayer.addAction(action); + } + + @Override + public void setAllowBadMoves(boolean allowBadMoves) { + computerPlayer.setAllowBadMoves(allowBadMoves); + } + + @Override + public boolean canPayLifeCost() { + return computerPlayer.canPayLifeCost(); + } + + @Override + public void setCanPayLifeCost(boolean canPayLifeCost) { + computerPlayer.setCanPayLifeCost(canPayLifeCost); + } + + @Override + public boolean canPaySacrificeCost() { + return computerPlayer.canPaySacrificeCost(); + } + + @Override + public void setCanPaySacrificeCost(boolean canPaySacrificeCost) { + computerPlayer.setCanPaySacrificeCost(canPaySacrificeCost); + } + + @Override + public boolean canLoseByZeroOrLessLife() { + return computerPlayer.canLoseByZeroOrLessLife(); + } + + @Override + public void setLoseByZeroOrLessLife(boolean loseByZeroOrLessLife) { + computerPlayer.setLoseByZeroOrLessLife(loseByZeroOrLessLife); + } + + @Override + public boolean canPlayCardsFromGraveyard() { + return computerPlayer.canPlayCardsFromGraveyard(); + } + + @Override + public void setPlayCardsFromGraveyard(boolean playCardsFromGraveyard) { + computerPlayer.setPlayCardsFromGraveyard(playCardsFromGraveyard); + } + + @Override + public boolean autoLoseGame() { + return computerPlayer.autoLoseGame(); + } + + @Override + public void becomesActivePlayer() { + computerPlayer.becomesActivePlayer(); + } + + @Override + public int getTurns() { + return computerPlayer.getTurns(); + } + + @Override + public int getStoredBookmark() { + return computerPlayer.getStoredBookmark(); + } + + @Override + public void setStoredBookmark(int storedBookmark) { + computerPlayer.setStoredBookmark(storedBookmark); + } + + @Override + public synchronized void resetStoredBookmark(Game game) { + computerPlayer.resetStoredBookmark(game); + } + + @Override + public void revealFaceDownCard(Card card, Game game) { + computerPlayer.revealFaceDownCard(card, game); + } + + @Override + public void setPriorityTimeLeft(int timeLeft) { + computerPlayer.setPriorityTimeLeft(timeLeft); + } + + @Override + public int getPriorityTimeLeft() { + return computerPlayer.getPriorityTimeLeft(); + } + + @Override + public boolean hasQuit() { + return computerPlayer.hasQuit(); + } + + @Override + public boolean hasTimerTimeout() { + return computerPlayer.hasTimerTimeout(); + } + + @Override + public boolean hasIdleTimeout() { + return computerPlayer.hasIdleTimeout(); + } + + @Override + public void setReachedNextTurnAfterLeaving(boolean reachedNextTurnAfterLeaving) { + computerPlayer.setReachedNextTurnAfterLeaving(reachedNextTurnAfterLeaving); + } + + @Override + public boolean hasReachedNextTurnAfterLeaving() { + return computerPlayer.hasReachedNextTurnAfterLeaving(); + } + + @Override + public boolean canJoinTable(Table table) { + return computerPlayer.canJoinTable(table); + } + + @Override + public void setCommanderId(UUID commanderId) { + computerPlayer.setCommanderId(commanderId); + } + + @Override + public UUID getCommanderId() { + return computerPlayer.getCommanderId(); + } + + @Override + public boolean moveCards(Cards cards, Zone fromZone, Zone toZone, Ability source, Game game) { + return computerPlayer.moveCards(cards, fromZone, toZone, source, game); + } + + @Override + public boolean moveCards(Card card, Zone fromZone, Zone toZone, Ability source, Game game) { + return computerPlayer.moveCards(card, fromZone, toZone, source, game); + } + + @Override + public boolean moveCards(List cards, Zone fromZone, Zone toZone, Ability source, Game game) { + return computerPlayer.moveCards(cards, fromZone, toZone, source, game); + } + + @Override + public boolean moveCardToHandWithInfo(Card card, UUID sourceId, Game game, Zone fromZone) { + return computerPlayer.moveCardToHandWithInfo(card, sourceId, game, fromZone); + } + + @Override + public boolean moveCardToHandWithInfo(Card card, UUID sourceId, Game game, Zone fromZone, boolean withName) { + return computerPlayer.moveCardToHandWithInfo(card, sourceId, game, fromZone, withName); + } + + @Override + public boolean moveCardsToGraveyardWithInfo(List allCards, Ability source, Game game, Zone fromZone) { + return computerPlayer.moveCardsToGraveyardWithInfo(allCards, source, game, fromZone); + } + + @Override + public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId, Game game, Zone fromZone) { + return computerPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone); + } + + @Override + public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId, Game game, Zone fromZone, boolean toTop, boolean withName) { + return computerPlayer.moveCardToLibraryWithInfo(card, sourceId, game, fromZone, toTop, withName); + } + + @Override + public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId, Game game, Zone fromZone, boolean withName) { + return computerPlayer.moveCardToExileWithInfo(card, exileId, exileName, sourceId, game, fromZone, withName); + } + + @Override + public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId) { + return computerPlayer.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId); + } + + @Override + public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped) { + return computerPlayer.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId, tapped); + } + + @Override + public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped, boolean facedown) { + return computerPlayer.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId, tapped, facedown); + } + + @Override + public boolean hasOpponent(UUID playerToCheckId, Game game) { + return computerPlayer.hasOpponent(playerToCheckId, game); + } + + @Override + public boolean getPassedAllTurns() { + return computerPlayer.getPassedAllTurns(); + } + + @Override + public boolean getPassedUntilNextMain() { + return computerPlayer.getPassedUntilNextMain(); + } + + @Override + public boolean getPassedUntilEndOfTurn() { + return computerPlayer.getPassedUntilEndOfTurn(); + } + + @Override + public boolean getPassedTurn() { + return computerPlayer.getPassedTurn(); + } + + @Override + public boolean getPassedUntilStackResolved() { + return computerPlayer.getPassedUntilStackResolved(); + } + + @Override + public void revokePermissionToSeeHandCards() { + computerPlayer.revokePermissionToSeeHandCards(); + } + + @Override + public void addPermissionToShowHandCards(UUID watcherUserId) { + computerPlayer.addPermissionToShowHandCards(watcherUserId); + } + + @Override + public boolean isRequestToShowHandCardsAllowed() { + return computerPlayer.isRequestToShowHandCardsAllowed(); + } + + @Override + public boolean hasUserPermissionToSeeHand(UUID userId) { + return computerPlayer.hasUserPermissionToSeeHand(userId); + } + + @Override + public Set getUsersAllowedToSeeHandCards() { + return computerPlayer.getUsersAllowedToSeeHandCards(); + } + + @Override + public void setMatchPlayer(MatchPlayer matchPlayer) { + computerPlayer.setMatchPlayer(matchPlayer); + } + + @Override + public MatchPlayer getMatchPlayer() { + return computerPlayer.getMatchPlayer(); + } + + @Override + public void cleanUpOnMatchEnd() { + computerPlayer.cleanUpOnMatchEnd(); + } + + @Override + public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) { + return computerPlayer.chooseSpellAbilityForCast(ability, game, noMana); + } + + @Override + public void skip() { + computerPlayer.skip(); + } + + @Override + public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) { + // needed to call here the TestPlayer because it's overwitten + return choose(outcome, target, sourceId, game, null); + } + + @Override + public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) { + return computerPlayer.choose(outcome, cards, target, game); + } + + @Override + public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) { + return computerPlayer.chooseTarget(outcome, cards, target, source, game); + } + + @Override + public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) { + return computerPlayer.chooseTargetAmount(outcome, target, source, game); + } + + @Override + public boolean chooseMulligan(Game game) { + return computerPlayer.chooseMulligan(game); + } + + @Override + public boolean choosePile(Outcome outcome, String message, List pile1, List pile2, Game game) { + return computerPlayer.choosePile(outcome, message, pile1, pile2, game); + } + + @Override + public boolean playMana(ManaCost unpaid, String promptText, Game game) { + return computerPlayer.playMana(unpaid, promptText, game); + } + + @Override + public UUID chooseAttackerOrder(List attacker, Game game) { + return computerPlayer.chooseAttackerOrder(attacker, game); + } + + @Override + public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { + return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); + } + + @Override + public void assignDamage(int damage, List targets, String singleTargetName, UUID sourceId, Game game) { + computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game); + } + + @Override + public void sideboard(Match match, Deck deck) { + computerPlayer.sideboard(match, deck); + } + + @Override + public void construct(Tournament tournament, Deck deck) { + computerPlayer.construct(tournament, deck); + } + + @Override + public void pickCard(List cards, Deck deck, Draft draft) { + computerPlayer.pickCard(cards, deck, draft); + } + + public void setAIPlayer(boolean AIPlayer) { + this.AIPlayer = AIPlayer; + } + + public boolean isAIPlayer() { + return AIPlayer; + } + } - diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java index 0e6390f9f05..230159d4d6d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java @@ -27,7 +27,7 @@ import java.util.Random; */ public class PlayGameTest extends MageTestBase { - private static List colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu"); + private final static List colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu"); @Ignore @Test @@ -67,7 +67,8 @@ public class PlayGameTest extends MageTestBase { long t1 = System.nanoTime(); GameOptions options = new GameOptions(); options.testMode = true; - game.start(computerA.getId(), options); + game.setGameOptions(options); + game.start(computerA.getId()); long t2 = System.nanoTime(); logger.info("Winner: " + game.getWinner()); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/TestPlayRandomGame.java b/Mage.Tests/src/test/java/org/mage/test/serverside/TestPlayRandomGame.java index 029eff40c7b..e409b738285 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/TestPlayRandomGame.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/TestPlayRandomGame.java @@ -27,7 +27,7 @@ import java.util.Random; */ public class TestPlayRandomGame extends MageTestBase { - private static List colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu"); + private final static List colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu"); @Test @Ignore @@ -58,12 +58,11 @@ public class TestPlayRandomGame extends MageTestBase { game.addPlayer(computerB, deck2); game.loadCards(deck2.getCards(), computerB.getId()); - boolean testMode = true; - long t1 = System.nanoTime(); GameOptions options = new GameOptions(); options.testMode = true; - game.start(computerA.getId(), options); + game.setGameOptions(options); + game.start(computerA.getId()); long t2 = System.nanoTime(); logger.info("Winner: " + game.getWinner()); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestAPI.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestAPI.java index 6369627bbc1..67cf6484b5f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestAPI.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestAPI.java @@ -1,13 +1,14 @@ package org.mage.test.serverside.base; -import mage.constants.PhaseStep; +import java.util.List; import mage.abilities.Ability; +import mage.constants.PhaseStep; import mage.constants.Zone; import mage.filter.Filter; import mage.players.Player; import org.mage.test.player.TestPlayer; -import java.util.List; + /** * Interface for all test initialization and assertion operations. @@ -81,11 +82,14 @@ public interface CardTestAPI { /** * Define turn number to stop the game on. + * @param turn */ void setStopOnTurn(int turn); /** * Define the turn number and step to stop the game on. + * @param turn + * @param step */ void setStopAt(int turn, PhaseStep step); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommanderDuelBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommanderDuelBase.java new file mode 100644 index 00000000000..c31449aa0e8 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommanderDuelBase.java @@ -0,0 +1,54 @@ +/* + * 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 org.mage.test.serverside.base; + +import java.io.FileNotFoundException; +import mage.constants.MultiplayerAttackOption; +import mage.constants.RangeOfInfluence; +import mage.game.CommanderDuel; +import mage.game.Game; +import mage.game.GameException; +import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; + +/** + * + * @author LevelX2 + */ + +public abstract class CardTestCommanderDuelBase extends CardTestPlayerAPIImpl { + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + Game game = new CommanderDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 40); + + playerA = createPlayer(game, playerA, "PlayerA","CommanderDuel.dck"); + playerB = createPlayer(game, playerB, "PlayerB","CommanderDuel.dck"); + return game; + } + +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestMultiPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestMultiPlayerBase.java index e891527b51c..36ededfe4a3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestMultiPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestMultiPlayerBase.java @@ -1,18 +1,14 @@ package org.mage.test.serverside.base; -import mage.constants.PhaseStep; import mage.cards.Card; import mage.cards.decks.Deck; import mage.cards.decks.importer.DeckImporterUtil; import mage.constants.MultiplayerAttackOption; import mage.constants.RangeOfInfluence; -import mage.filter.Filter; import mage.game.*; import mage.game.permanent.Permanent; import mage.players.Player; import org.junit.Assert; -import org.junit.Before; -import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; import java.io.File; @@ -26,274 +22,15 @@ import java.io.FileNotFoundException; */ public abstract class CardTestMultiPlayerBase extends CardTestPlayerAPIImpl { - protected enum ExpectedType { - TURN_NUMBER, - RESULT, - LIFE, - BATTLEFIELD, - GRAVEYARD, - UNKNOWN - } - - protected GameOptions gameOptions; - - @Before - public void reset() throws GameException, FileNotFoundException { - if (currentGame != null) { - logger.debug("Resetting previous game and creating new one!"); - currentGame = null; - System.gc(); - } - + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { Game game = new FreeForAll(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 20); playerA = createPlayer(game, playerA, "PlayerA"); playerB = createPlayer(game, playerB, "PlayerB"); playerC = createPlayer(game, playerC, "PlayerC"); playerD = createPlayer(game, playerD, "PlayerD"); - - activePlayer = playerA; - currentGame = game; - - stopOnTurn = 2; - stopAtStep = PhaseStep.UNTAP; - - for (Player player : currentGame.getPlayers().values()) { - TestPlayer testPlayer = (TestPlayer)player; - getCommands(testPlayer).clear(); - getLibraryCards(testPlayer).clear(); - getHandCards(testPlayer).clear(); - getBattlefieldCards(testPlayer).clear(); - getGraveCards(testPlayer).clear(); - } - - gameOptions = new GameOptions(); - } - - public void load(String path) throws FileNotFoundException, GameException { - String cardPath = TESTS_PATH + path; - File checkFile = new File(cardPath); - if (!checkFile.exists()) { - throw new FileNotFoundException("Couldn't find test file: " + cardPath); - } - if (checkFile.isDirectory()) { - throw new FileNotFoundException("Couldn't find test file: " + cardPath + ". It is directory."); - } - - if (currentGame != null) { - logger.debug("Resetting previous game and creating new one!"); - currentGame = null; - System.gc(); - } - - Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, 0, 20); - - playerA = createNewPlayer("ComputerA"); - playerA.setTestMode(true); - - Deck deck = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); - - if (deck.getCards().size() < 40) { - throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); - } - game.addPlayer(playerA, deck); - game.loadCards(deck.getCards(), playerA.getId()); - - playerB = createNewPlayer("ComputerB"); - playerB.setTestMode(true); - Deck deck2 = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); - if (deck2.getCards().size() < 40) { - throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck2.getCards().size()); - } - game.addPlayer(playerB, deck2); - game.loadCards(deck2.getCards(), playerB.getId()); - - parseScenario(cardPath); - - activePlayer = playerA; - currentGame = game; - } - - /** - * Starts testing card by starting current game. - * - * @throws IllegalStateException In case game wasn't created previously. Use {@link #load} method to initialize the game. - */ - public void execute() throws IllegalStateException { - if (currentGame == null || activePlayer == null) { - throw new IllegalStateException("Game is not initialized. Use load method to load a test case and initialize a game."); - } - - for (Player player : currentGame.getPlayers().values()) { - TestPlayer testPlayer = (TestPlayer)player; - currentGame.cheat(player.getId(), getCommands(testPlayer)); - currentGame.cheat(player.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer), - getBattlefieldCards(testPlayer), getGraveCards(testPlayer)); - } - - boolean testMode = true; - long t1 = System.nanoTime(); - - gameOptions.testMode = true; - gameOptions.stopOnTurn = stopOnTurn; - gameOptions.stopAtStep = stopAtStep; - currentGame.start(activePlayer.getId(), gameOptions); - long t2 = System.nanoTime(); - logger.debug("Winner: " + currentGame.getWinner()); - logger.info("Test has been executed. Execution time: " + (t2 - t1) / 1000000 + " ms"); - - assertTheResults(); - } - - /** - * Assert expected and actual results. - */ - private void assertTheResults() { - logger.debug("Matching expected results:"); - for (String line : expectedResults) { - boolean ok = false; - try { - ExpectedType type = getExpectedType(line); - if (type.equals(CardTestMultiPlayerBase.ExpectedType.UNKNOWN)) { - throw new AssertionError("Unknown expected type, check the line in $expected section=" + line); - } - parseType(type, line); - ok = true; - } finally { - logger.info(" " + line + " - " + (ok ? "OK" : "ERROR")); - } - } - } - - private ExpectedType getExpectedType(String line) { - if (line.startsWith("turn:")) { - return CardTestMultiPlayerBase.ExpectedType.TURN_NUMBER; - } - if (line.startsWith("result:")) { - return CardTestMultiPlayerBase.ExpectedType.RESULT; - } - if (line.startsWith("life:")) { - return CardTestMultiPlayerBase.ExpectedType.LIFE; - } - if (line.startsWith("battlefield:")) { - return CardTestMultiPlayerBase.ExpectedType.BATTLEFIELD; - } - if (line.startsWith("graveyard:")) { - return CardTestMultiPlayerBase.ExpectedType.GRAVEYARD; - } - return CardTestMultiPlayerBase.ExpectedType.UNKNOWN; - } - - private void parseType(ExpectedType type, String line) { - if (type.equals(CardTestMultiPlayerBase.ExpectedType.TURN_NUMBER)) { - int turn = getIntParam(line, 1); - Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum()); - return; - } - if (type.equals(CardTestMultiPlayerBase.ExpectedType.RESULT)) { - String expected = getStringParam(line, 1); - String actual = "draw"; - if (currentGame.getWinner().equals("Player ComputerA is the winner")) { - actual = "won"; - } else if (currentGame.getWinner().equals("Player ComputerB is the winner")) { - actual = "lost"; - } - Assert.assertEquals("Game results are not equal", expected, actual); - return; - } - if (type.equals(CardTestMultiPlayerBase.ExpectedType.LIFE)) { - String player = getStringParam(line, 1); - int expected = getIntParam(line, 2); - if (player.equals("ComputerA")) { - int actual = currentGame.getPlayer(playerA.getId()).getLife(); - Assert.assertEquals("Life amounts are not equal", expected, actual); - } else if (player.equals("ComputerB")) { - int actual = currentGame.getPlayer(playerB.getId()).getLife(); - Assert.assertEquals("Life amounts are not equal", expected, actual); - } else { - throw new IllegalArgumentException("Wrong player in 'life' line, player=" + player + ", line=" + line); - } - return; - } - if (type.equals(CardTestMultiPlayerBase.ExpectedType.BATTLEFIELD)) { - String playerName = getStringParam(line, 1); - String cardName = getStringParam(line, 2); - int expectedCount = getIntParam(line, 3); - Player player = null; - if (playerName.equals("ComputerA")) { - player = currentGame.getPlayer(playerA.getId()); - } else if (playerName.equals("ComputerB")) { - player = currentGame.getPlayer(playerB.getId()); - } else { - throw new IllegalArgumentException("Wrong player in 'battlefield' line, player=" + player + ", line=" + line); - } - int actualCount = 0; - for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) { - if (permanent.getControllerId().equals(player.getId())) { - if (permanent.getName().equals(cardName)) { - actualCount++; - } - } - } - Assert.assertEquals("(Battlefield) Card counts are not equal (" + cardName + ")", expectedCount, actualCount); - return; - } - if (type.equals(CardTestMultiPlayerBase.ExpectedType.GRAVEYARD)) { - String playerName = getStringParam(line, 1); - String cardName = getStringParam(line, 2); - int expectedCount = getIntParam(line, 3); - Player player = null; - if (playerName.equals("ComputerA")) { - player = currentGame.getPlayer(playerA.getId()); - } else if (playerName.equals("ComputerB")) { - player = currentGame.getPlayer(playerB.getId()); - } else { - throw new IllegalArgumentException("Wrong player in 'graveyard' line, player=" + player + ", line=" + line); - } - int actualCount = 0; - for (Card card : player.getGraveyard().getCards(currentGame)) { - if (card.getName().equals(cardName)) { - actualCount++; - } - } - Assert.assertEquals("(Graveyard) Card counts are not equal (" + cardName + ")", expectedCount, actualCount); - } - } - - private int getIntParam(String line, int index) { - String[] params = line.split(":"); - if (index > params.length - 1) { - throw new IllegalArgumentException("Not correct line: " + line); - } - return Integer.parseInt(params[index]); - } - - private String getStringParam(String line, int index) { - String[] params = line.split(":"); - if (index > params.length - 1) { - throw new IllegalArgumentException("Not correct line: " + line); - } - return params[index]; - } - - protected void checkPermanentPT(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) { - if (currentGame == null) { - throw new IllegalStateException("Current game is null"); - } - if (scope.equals(Filter.ComparisonScope.All)) { - throw new UnsupportedOperationException("ComparisonScope.All is not implemented."); - } - - for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) { - if (permanent.getName().equals(cardName)) { - Assert.assertEquals("Power is not the same", power, permanent.getPower().getValue()); - Assert.assertEquals("Toughness is not the same", toughness, permanent.getToughness().getValue()); - break; - } - } - } - - protected void skipInitShuffling() { - gameOptions.skipInitShuffling = true; + return game; } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java index 68c08699bce..e3435c3d3f4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java @@ -1,27 +1,11 @@ package org.mage.test.serverside.base; -import java.io.File; import java.io.FileNotFoundException; -import mage.cards.Card; -import mage.cards.decks.Deck; -import mage.cards.decks.importer.DeckImporterUtil; import mage.constants.MultiplayerAttackOption; -import mage.constants.PhaseStep; import mage.constants.RangeOfInfluence; -import mage.filter.Filter; import mage.game.Game; import mage.game.GameException; -import mage.game.GameOptions; import mage.game.TwoPlayerDuel; -import mage.game.permanent.Permanent; -import mage.players.Player; -import org.junit.Assert; -import org.junit.Before; -import org.mage.test.player.TestPlayer; -import static org.mage.test.serverside.base.MageTestPlayerBase.TESTS_PATH; -import static org.mage.test.serverside.base.MageTestPlayerBase.activePlayer; -import static org.mage.test.serverside.base.MageTestPlayerBase.currentGame; -import static org.mage.test.serverside.base.MageTestPlayerBase.logger; import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; /** @@ -30,293 +14,14 @@ import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; * @author ayratn */ public abstract class CardTestPlayerBase extends CardTestPlayerAPIImpl { - - public static final String NO_TARGET = "NO_TARGET"; - - protected enum ExpectedType { - TURN_NUMBER, - RESULT, - LIFE, - BATTLEFIELD, - GRAVEYARD, - UNKNOWN - } - - protected GameOptions gameOptions; - - public CardTestPlayerBase() { - } - + @Override - protected TestPlayer createNewPlayer(String playerName) { - return createPlayer(playerName); - } - - @Before - public void reset() throws GameException, FileNotFoundException { - if (currentGame != null) { - logger.debug("Resetting previous game and creating new one!"); - currentGame = null; - System.gc(); - } - + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 20); - playerA = createNewPlayer("PlayerA"); - playerA.setTestMode(true); - logger.debug("Loading deck..."); - Deck deck = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); - logger.debug("Done!"); - if (deck.getCards().size() < 40) { - throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); - } - game.loadCards(deck.getCards(), playerA.getId()); - game.addPlayer(playerA, deck); + playerA = createPlayer(game, playerA, "PlayerA"); + playerB = createPlayer(game, playerB, "PlayerB"); + return game; + } - playerB = createNewPlayer("PlayerB"); - playerB.setTestMode(true); - Deck deck2 = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); - if (deck2.getCards().size() < 40) { - throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck2.getCards().size()); - } - game.loadCards(deck2.getCards(), playerB.getId()); - game.addPlayer(playerB, deck2); - activePlayer = playerA; - currentGame = game; - - stopOnTurn = 2; - stopAtStep = PhaseStep.UNTAP; - - for (Player player : currentGame.getPlayers().values()) { - TestPlayer testPlayer = (TestPlayer)player; - getCommands(testPlayer).clear(); - getLibraryCards(testPlayer).clear(); - getHandCards(testPlayer).clear(); - getBattlefieldCards(testPlayer).clear(); - getGraveCards(testPlayer).clear(); - } - - gameOptions = new GameOptions(); - } - - public void load(String path) throws FileNotFoundException, GameException { - String cardPath = TESTS_PATH + path; - File checkFile = new File(cardPath); - if (!checkFile.exists()) { - throw new FileNotFoundException("Couldn't find test file: " + cardPath); - } - if (checkFile.isDirectory()) { - throw new FileNotFoundException("Couldn't find test file: " + cardPath + ". It is directory."); - } - - if (currentGame != null) { - logger.debug("Resetting previous game and creating new one!"); - currentGame = null; - System.gc(); - } - - Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, 0, 20); - - playerA = createNewPlayer("ComputerA"); - playerA.setTestMode(true); - - Deck deck = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); - - if (deck.getCards().size() < 40) { - throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); - } - game.addPlayer(playerA, deck); - game.loadCards(deck.getCards(), playerA.getId()); - - playerB = createNewPlayer("ComputerB"); - playerB.setTestMode(true); - Deck deck2 = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); - if (deck2.getCards().size() < 40) { - throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck2.getCards().size()); - } - game.addPlayer(playerB, deck2); - game.loadCards(deck2.getCards(), playerB.getId()); - - parseScenario(cardPath); - - activePlayer = playerA; - currentGame = game; - } - - /** - * Starts testing card by starting current game. - * - * @throws IllegalStateException In case game wasn't created previously. Use {@link #load} method to initialize the game. - */ - public void execute() throws IllegalStateException { - if (currentGame == null || activePlayer == null) { - throw new IllegalStateException("Game is not initialized. Use load method to load a test case and initialize a game."); - } - - for (Player player : currentGame.getPlayers().values()) { - TestPlayer testPlayer = (TestPlayer)player; - currentGame.cheat(player.getId(), getCommands(testPlayer)); - currentGame.cheat(player.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer), - getBattlefieldCards(testPlayer), getGraveCards(testPlayer)); - } - - boolean testMode = true; - long t1 = System.nanoTime(); - - gameOptions.testMode = true; - gameOptions.stopOnTurn = stopOnTurn; - gameOptions.stopAtStep = stopAtStep; - currentGame.start(activePlayer.getId(), gameOptions); - long t2 = System.nanoTime(); - logger.debug("Winner: " + currentGame.getWinner()); - logger.info("Test has been executed. Execution time: " + (t2 - t1) / 1000000 + " ms"); - - assertTheResults(); - } - - /** - * Assert expected and actual results. - */ - private void assertTheResults() { - logger.debug("Matching expected results:"); - for (String line : expectedResults) { - boolean ok = false; - try { - ExpectedType type = getExpectedType(line); - if (type.equals(CardTestPlayerBase.ExpectedType.UNKNOWN)) { - throw new AssertionError("Unknown expected type, check the line in $expected section=" + line); - } - parseType(type, line); - ok = true; - } finally { - logger.info(" " + line + " - " + (ok ? "OK" : "ERROR")); - } - } - } - - private ExpectedType getExpectedType(String line) { - if (line.startsWith("turn:")) { - return CardTestPlayerBase.ExpectedType.TURN_NUMBER; - } - if (line.startsWith("result:")) { - return CardTestPlayerBase.ExpectedType.RESULT; - } - if (line.startsWith("life:")) { - return CardTestPlayerBase.ExpectedType.LIFE; - } - if (line.startsWith("battlefield:")) { - return CardTestPlayerBase.ExpectedType.BATTLEFIELD; - } - if (line.startsWith("graveyard:")) { - return CardTestPlayerBase.ExpectedType.GRAVEYARD; - } - return CardTestPlayerBase.ExpectedType.UNKNOWN; - } - - private void parseType(ExpectedType type, String line) { - if (type.equals(CardTestPlayerBase.ExpectedType.TURN_NUMBER)) { - int turn = getIntParam(line, 1); - Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum()); - return; - } - if (type.equals(CardTestPlayerBase.ExpectedType.RESULT)) { - String expected = getStringParam(line, 1); - String actual = "draw"; - switch (currentGame.getWinner()) { - case "Player ComputerA is the winner": - actual = "won"; - break; - case "Player ComputerB is the winner": - actual = "lost"; - break; - } - Assert.assertEquals("Game results are not equal", expected, actual); - return; - } - - Player player = null; - String playerName = getStringParam(line, 1); - switch (playerName) { - case "ComputerA": - player = currentGame.getPlayer(playerA.getId()); - break; - case "ComputerB": - player = currentGame.getPlayer(playerB.getId()); - break; - } - if (player == null) { - throw new IllegalArgumentException("Wrong player in 'battlefield' line, player=" + player + ", line=" + line); - } - - if (type.equals(CardTestPlayerBase.ExpectedType.LIFE)) { - int expected = getIntParam(line, 2); - int actual = player.getLife(); - Assert.assertEquals("Life amounts are not equal", expected, actual); - return; - } - - if (type.equals(CardTestPlayerBase.ExpectedType.BATTLEFIELD)) { - String cardName = getStringParam(line, 2); - int expectedCount = getIntParam(line, 3); - int actualCount = 0; - for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) { - if (permanent.getControllerId().equals(player.getId())) { - if (permanent.getName().equals(cardName)) { - actualCount++; - } - } - } - Assert.assertEquals("(Battlefield) Card counts are not equal (" + cardName + ")", expectedCount, actualCount); - return; - } - - if (type.equals(CardTestPlayerBase.ExpectedType.GRAVEYARD)) { - String cardName = getStringParam(line, 2); - int expectedCount = getIntParam(line, 3); - int actualCount = 0; - for (Card card : player.getGraveyard().getCards(currentGame)) { - if (card.getName().equals(cardName)) { - actualCount++; - } - } - Assert.assertEquals("(Graveyard) Card counts are not equal (" + cardName + ")", expectedCount, actualCount); - } - } - - private int getIntParam(String line, int index) { - String[] params = line.split(":"); - if (index > params.length - 1) { - throw new IllegalArgumentException("Not correct line: " + line); - } - return Integer.parseInt(params[index]); - } - - private String getStringParam(String line, int index) { - String[] params = line.split(":"); - if (index > params.length - 1) { - throw new IllegalArgumentException("Not correct line: " + line); - } - return params[index]; - } - - protected void checkPermanentPT(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) { - if (currentGame == null) { - throw new IllegalStateException("Current game is null"); - } - if (scope.equals(Filter.ComparisonScope.All)) { - throw new UnsupportedOperationException("ComparisonScope.All is not implemented."); - } - - for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) { - if (permanent.getName().equals(cardName)) { - Assert.assertEquals("Power is not the same", power, permanent.getPower().getValue()); - Assert.assertEquals("Toughness is not the same", toughness, permanent.getToughness().getValue()); - break; - } - } - } - - protected void skipInitShuffling() { - gameOptions.skipInitShuffling = true; - } } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java new file mode 100644 index 00000000000..2f50da22db0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java @@ -0,0 +1,71 @@ +/* + * 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 org.mage.test.serverside.base; + +import java.io.FileNotFoundException; +import mage.constants.MultiplayerAttackOption; +import mage.constants.RangeOfInfluence; +import mage.game.Game; +import mage.game.GameException; +import mage.game.TwoPlayerDuel; +import mage.player.ai.ComputerPlayer7; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; + +/** + * + * @author LevelX2 + */ + +public abstract class CardTestPlayerBaseAI extends CardTestPlayerAPIImpl { + + int skill = 9; + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 20); + + playerA = createPlayer(game, playerA, "PlayerA"); + playerB = createPlayer(game, playerB, "PlayerB"); + return game; + } + + @Override + protected TestPlayer createPlayer(String name) { + if (name.equals("PlayerA")) { + TestPlayer testPlayer = new TestPlayer(new ComputerPlayer7("PlayerA", RangeOfInfluence.ONE, skill)); + testPlayer.setAIPlayer(true); + return testPlayer; + } + return super.createPlayer(name); + } + + public void setAISkill(int skill) { + this.skill = skill; + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java index d1864fbf4ac..bcfa18d0f54 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java @@ -31,7 +31,6 @@ import java.io.FilenameFilter; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.mage.test.serverside.base.MageTestPlayerBase.currentGame; /** * Base class for all tests. diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java index ace998ed9b8..32805289ed6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java @@ -28,6 +28,7 @@ import java.io.FilenameFilter; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import mage.player.ai.ComputerPlayer; /** * Base class for all tests. @@ -260,14 +261,15 @@ public abstract class MageTestPlayerBase { } private TestPlayer getPlayer(String name) { - if (name.equals("ComputerA")) { - return playerA; - } else if (name.equals("ComputerB")) { - return playerB; - } else if (name.equals("ComputerC")) { - return playerC; - } else if (name.equals("ComputerD")) { - return playerD; + switch (name) { + case "ComputerA": + return playerA; + case "ComputerB": + return playerB; + case "ComputerC": + return playerC; + case "ComputerD": + return playerD; } throw new IllegalArgumentException("Couldn't find player for name=" + name); } @@ -339,6 +341,7 @@ public abstract class MageTestPlayerBase { } protected TestPlayer createPlayer(String name) { - return new TestPlayer(name, RangeOfInfluence.ONE); + return new TestPlayer(new ComputerPlayer(name, RangeOfInfluence.ONE)); } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java index 817d7fe6c2c..66423135b0f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java @@ -28,6 +28,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP /** * Default game initialization params for red player (that plays with Mountains) */ + @Override public void useRedDefault() { // *** ComputerA *** // battlefield:ComputerA:Mountain:5 @@ -88,6 +89,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param player {@link Player} to add cards for. Use either playerA or playerB. * @param cardName Card name in string format. */ + @Override public void addCard(Zone gameZone, TestPlayer player, String cardName) { addCard(gameZone, player, cardName, 1, false); } @@ -100,6 +102,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param cardName Card name in string format. * @param count Amount of cards to be added. */ + @Override public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) { addCard(gameZone, player, cardName, count, false); } @@ -114,6 +117,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped. * In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown */ + @Override public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) { @@ -179,6 +183,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param player {@link Player} to set life count for. * @param life Life count to set. */ + @Override public void setLife(TestPlayer player, int life) { if (player.equals(playerA)) { commandsA.put(Zone.OUTSIDE, "life:" + String.valueOf(life)); @@ -190,16 +195,18 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP /** * Define turn number to stop the game on. */ + @Override public void setStopOnTurn(int turn) { - stopOnTurn = turn == -1 ? null : Integer.valueOf(turn); + stopOnTurn = turn == -1 ? null : turn; stopAtStep = PhaseStep.UNTAP; } /** * Define turn number and step to stop the game on. */ + @Override public void setStopAt(int turn, PhaseStep step) { - stopOnTurn = turn == -1 ? null : Integer.valueOf(turn); + stopOnTurn = turn == -1 ? null : turn; stopAtStep = step; } @@ -208,6 +215,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * * @param turn Expected turn number to compare with. 1-based. */ + @Override public void assertTurn(int turn) throws AssertionError { Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum()); } @@ -217,21 +225,28 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * * @param result Expected {@link GameResult} to compare with. */ + @Override public void assertResult(Player player, GameResult result) throws AssertionError { if (player.equals(playerA)) { GameResult actual = CardTestAPI.GameResult.DRAW; - if (currentGame.getWinner().equals("Player PlayerA is the winner")) { - actual = CardTestAPI.GameResult.WON; - } else if (currentGame.getWinner().equals("Player PlayerB is the winner")) { - actual = CardTestAPI.GameResult.LOST; + switch (currentGame.getWinner()) { + case "Player PlayerA is the winner": + actual = CardTestAPI.GameResult.WON; + break; + case "Player PlayerB is the winner": + actual = CardTestAPI.GameResult.LOST; + break; } Assert.assertEquals("Game results are not equal", result, actual); } else if (player.equals(playerB)) { GameResult actual = CardTestAPI.GameResult.DRAW; - if (currentGame.getWinner().equals("Player PlayerB is the winner")) { - actual = CardTestAPI.GameResult.WON; - } else if (currentGame.getWinner().equals("Player PlayerA is the winner")) { - actual = CardTestAPI.GameResult.LOST; + switch (currentGame.getWinner()) { + case "Player PlayerB is the winner": + actual = CardTestAPI.GameResult.WON; + break; + case "Player PlayerA is the winner": + actual = CardTestAPI.GameResult.LOST; + break; } Assert.assertEquals("Game results are not equal", result, actual); } @@ -243,6 +258,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param player {@link Player} to get life for comparison. * @param life Expected player's life to compare with. */ + @Override public void assertLife(Player player, int life) throws AssertionError { int actual = currentGame.getPlayer(player.getId()).getLife(); Assert.assertEquals("Life amounts are not equal", life, actual); @@ -265,6 +281,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you want "at least one creature with given name should have specified p\t" * Use ALL, if you want "all creature with gived name should have specified p\t" */ + @Override public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) throws AssertionError { int count = 0; @@ -298,6 +315,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP /** * {@inheritDoc} */ + @Override public void assertAbilities(Player player, String cardName, List abilities) throws AssertionError { int count = 0; @@ -326,6 +344,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param player {@link Player} which permanents should be counted. * @param count Expected count. */ + @Override public void assertPermanentCount(Player player, int count) throws AssertionError { int actualCount = 0; for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) { @@ -343,6 +362,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param cardName Name of the cards that should be counted. * @param count Expected count. */ + @Override public void assertPermanentCount(Player player, String cardName, int count) throws AssertionError { int actualCount = 0; for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) { diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index 1a3774626e3..dc059ca7cf6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -1,5 +1,8 @@ package org.mage.test.serverside.base.impl; +import java.io.FileNotFoundException; +import java.util.List; +import java.util.UUID; import mage.abilities.Ability; import mage.cards.Card; import mage.cards.decks.Deck; @@ -17,18 +20,18 @@ import mage.filter.predicate.mageobject.NamePredicate; import mage.game.ExileZone; import mage.game.Game; import mage.game.GameException; +import mage.game.GameOptions; import mage.game.command.CommandObject; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; import mage.players.Player; import org.junit.Assert; +import org.junit.Before; import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestAPI; +import org.mage.test.serverside.base.CardTestAPI.GameResult; import org.mage.test.serverside.base.MageTestPlayerBase; -import java.util.List; -import java.util.UUID; - /** * API for test initialization and asserting the test results. * @@ -36,6 +39,20 @@ import java.util.UUID; */ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implements CardTestAPI { + // Defines the constant if for activate ability is not target but a ability on the stack to define + public static final String NO_TARGET = "NO_TARGET"; + + protected GameOptions gameOptions; + + protected enum ExpectedType { + TURN_NUMBER, + RESULT, + LIFE, + BATTLEFIELD, + GRAVEYARD, + UNKNOWN + } + static { // CardScanner.scanned = true; CardScanner.scan(); @@ -83,33 +100,119 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement removeAllCardsFromLibrary(playerB); addCard(Zone.LIBRARY, playerB, "Plains", 10); } + + @Before + public void reset() throws GameException, FileNotFoundException { + if (currentGame != null) { + logger.debug("Resetting previous game and creating new one!"); + currentGame = null; + System.gc(); + } + currentGame = createNewGameAndPlayers(); + + activePlayer = playerA; + + stopOnTurn = 2; + stopAtStep = PhaseStep.UNTAP; + + for (Player player : currentGame.getPlayers().values()) { + TestPlayer testPlayer = (TestPlayer)player; + getCommands(testPlayer).clear(); + getLibraryCards(testPlayer).clear(); + getHandCards(testPlayer).clear(); + getBattlefieldCards(testPlayer).clear(); + getGraveCards(testPlayer).clear(); + } + + gameOptions = new GameOptions(); + } + + abstract protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException; + protected TestPlayer createPlayer(Game game, TestPlayer player, String name) throws GameException { + return createPlayer(game, player, name, "RB Aggro.dck"); + } + + protected TestPlayer createPlayer(Game game, TestPlayer player, String name, String deckName) throws GameException { player = createNewPlayer(name); player.setTestMode(true); logger.debug("Loading deck..."); - Deck deck = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); + Deck deck = Deck.load(DeckImporterUtil.importDeck(deckName), false, false); logger.debug("Done!"); if (deck.getCards().size() < 40) { throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); } - game.addPlayer(player, deck); game.loadCards(deck.getCards(), player.getId()); + game.loadCards(deck.getSideboard(), player.getId()); + game.addPlayer(player, deck); return player; } + /** + * Starts testing card by starting current game. + * + * @throws IllegalStateException In case game wasn't created previously. Use {@link #load} method to initialize the game. + */ + public void execute() throws IllegalStateException { + if (currentGame == null || activePlayer == null) { + throw new IllegalStateException("Game is not initialized. Use load method to load a test case and initialize a game."); + } + + for (Player player : currentGame.getPlayers().values()) { + TestPlayer testPlayer = (TestPlayer)player; + currentGame.cheat(player.getId(), getCommands(testPlayer)); + currentGame.cheat(player.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer), + getBattlefieldCards(testPlayer), getGraveCards(testPlayer)); + } + + long t1 = System.nanoTime(); + + gameOptions.testMode = true; + gameOptions.stopOnTurn = stopOnTurn; + gameOptions.stopAtStep = stopAtStep; + currentGame.setGameOptions(gameOptions); + currentGame.start(activePlayer.getId()); + long t2 = System.nanoTime(); + logger.debug("Winner: " + currentGame.getWinner()); + logger.info("Test has been executed. Execution time: " + (t2 - t1) / 1000000 + " ms"); + + } + + + protected TestPlayer createNewPlayer(String playerName) { return createPlayer(playerName); } - + + protected Player getPlayerFromName(String playerName, String line) { + Player player = null; + switch (playerName) { + case "ComputerA": + player = currentGame.getPlayer(playerA.getId()); + break; + case "ComputerB": + player = currentGame.getPlayer(playerB.getId()); + break; + case "ComputerC": + player = currentGame.getPlayer(playerC.getId()); + break; + case "ComputerD": + player = currentGame.getPlayer(playerD.getId()); + break; + default: + throw new IllegalArgumentException("Wrong player in 'battlefield' line, player=" + player + ", line=" + line); + } + return player; + } + /** * Removes all cards from player's library from the game. * Usually this should be used once before initialization to form the library in certain order. * * @param player {@link Player} to remove all library cards from. */ - @Override public void removeAllCardsFromLibrary(TestPlayer player) { getCommands(player).put(Zone.LIBRARY, "clear"); } @@ -131,7 +234,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param player {@link Player} to add cards for. Use either playerA or playerB. * @param cardName Card name in string format. */ - @Override public void addCard(Zone gameZone, TestPlayer player, String cardName) { addCard(gameZone, player, cardName, 1, false); } @@ -144,7 +246,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param cardName Card name in string format. * @param count Amount of cards to be added. */ - @Override public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) { addCard(gameZone, player, cardName, count, false); } @@ -159,7 +260,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped. * In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown */ - @Override public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) { if (gameZone.equals(Zone.BATTLEFIELD)) { @@ -214,7 +314,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param player {@link Player} to set life count for. * @param life Life count to set. */ - @Override public void setLife(TestPlayer player, int life) { getCommands(player).put(Zone.OUTSIDE, "life:" + String.valueOf(life)); } @@ -747,6 +846,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement player.addAction(turnNum, step, "activate:Cast " + cardName + "$targetPlayer=" + target.getName() + "$manaInPool=" + manaInPool); } + /** + * + * @param turnNum + * @param step + * @param player + * @param cardName + * @param targetName for modes you can add "mode=3" before target name, multiple targets can be seperated by ^ + */ public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) { player.addAction(turnNum, step, "activate:Cast " + cardName + "$target=" + targetName); } @@ -843,6 +950,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public void attack(int turnNum, TestPlayer player, String attacker) { player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:"+attacker); } + + public void attack(int turnNum, TestPlayer player, String attacker, TestPlayer defendingPlayer) { + player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:"+attacker+"$defendingPlayer="+defendingPlayer.getName()); + } public void attack(int turnNum, TestPlayer player, String attacker, String planeswalker) { player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, new StringBuilder("attack:").append(attacker).append("$planeswalker=").append(planeswalker).toString()); @@ -897,5 +1008,61 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public void addTarget(TestPlayer player, TestPlayer targetPlayer) { player.addTarget("targetPlayer="+targetPlayer.getName()); } + + protected void skipInitShuffling() { + gameOptions.skipInitShuffling = true; + } + + protected ExpectedType getExpectedType(String line) { + if (line.startsWith("turn:")) { + return ExpectedType.TURN_NUMBER; + } + if (line.startsWith("result:")) { + return ExpectedType.RESULT; + } + if (line.startsWith("life:")) { + return ExpectedType.LIFE; + } + if (line.startsWith("battlefield:")) { + return ExpectedType.BATTLEFIELD; + } + if (line.startsWith("graveyard:")) { + return ExpectedType.GRAVEYARD; + } + return ExpectedType.UNKNOWN; + } + + protected String getStringParam(String line, int index) { + String[] params = line.split(":"); + if (index > params.length - 1) { + throw new IllegalArgumentException("Not correct line: " + line); + } + return params[index]; + } + + protected void checkPermanentPT(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) { + if (currentGame == null) { + throw new IllegalStateException("Current game is null"); + } + if (scope.equals(Filter.ComparisonScope.All)) { + throw new UnsupportedOperationException("ComparisonScope.All is not implemented."); + } + for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) { + if (permanent.getName().equals(cardName)) { + Assert.assertEquals("Power is not the same", power, permanent.getPower().getValue()); + Assert.assertEquals("Toughness is not the same", toughness, permanent.getToughness().getValue()); + break; + } + } + } + + protected int getIntParam(String line, int index) { + String[] params = line.split(":"); + if (index > params.length - 1) { + throw new IllegalArgumentException("Not correct line: " + line); + } + return Integer.parseInt(params[index]); + } + } diff --git a/Mage/src/mage/MageObject.java b/Mage/src/mage/MageObject.java index 0694e0dfd7f..c2bd51447c8 100644 --- a/Mage/src/mage/MageObject.java +++ b/Mage/src/mage/MageObject.java @@ -26,7 +26,7 @@ public interface MageObject extends MageItem, Serializable { Abilities getAbilities(); boolean hasAbility(UUID abilityId, Game game); - ObjectColor getColor(); + ObjectColor getColor(Game game); ManaCosts getManaCost(); MageInt getPower(); diff --git a/Mage/src/mage/MageObjectImpl.java b/Mage/src/mage/MageObjectImpl.java index 075dfe1dc62..1d0c6400bcb 100644 --- a/Mage/src/mage/MageObjectImpl.java +++ b/Mage/src/mage/MageObjectImpl.java @@ -157,7 +157,7 @@ public abstract class MageObjectImpl implements MageObject { } @Override - public ObjectColor getColor() { + public ObjectColor getColor(Game game) { return color; } diff --git a/Mage/src/mage/abilities/Ability.java b/Mage/src/mage/abilities/Ability.java index a4c67c4dc75..81393981ce2 100644 --- a/Mage/src/mage/abilities/Ability.java +++ b/Mage/src/mage/abilities/Ability.java @@ -537,4 +537,6 @@ public interface Ability extends Controllable, Serializable { */ MageObject getSourceObjectIfItStillExists(Game game); + + String getTargetDescription(Targets targets, Game game); } diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 50f08e26194..f9847aa8b7e 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -352,10 +352,18 @@ public abstract class AbilityImpl implements Ability { //20100716 - 601.2e if (sourceObject != null) { sourceObject.adjustCosts(this, game); - for (Ability ability : sourceObject.getAbilities()) { - if (ability instanceof AdjustingSourceCosts) { - ((AdjustingSourceCosts)ability).adjustCosts(this, game); - } + if (sourceObject instanceof Card) { + for (Ability ability : ((Card)sourceObject).getAbilities(game)) { + if (ability instanceof AdjustingSourceCosts) { + ((AdjustingSourceCosts)ability).adjustCosts(this, game); + } + } + } else { + for (Ability ability : sourceObject.getAbilities()) { + if (ability instanceof AdjustingSourceCosts) { + ((AdjustingSourceCosts)ability).adjustCosts(this, game); + } + } } } @@ -1074,6 +1082,7 @@ public abstract class AbilityImpl implements Ability { return sb.toString(); } + @Override public String getTargetDescription(Targets targets, Game game) { return getTargetDescriptionForLog(targets, game); } diff --git a/Mage/src/mage/abilities/StateTriggeredAbility.java b/Mage/src/mage/abilities/StateTriggeredAbility.java index 6b0d9e6f613..1c8cfadfb37 100644 --- a/Mage/src/mage/abilities/StateTriggeredAbility.java +++ b/Mage/src/mage/abilities/StateTriggeredAbility.java @@ -32,6 +32,7 @@ import java.util.UUID; import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.game.Game; +import mage.game.events.GameEvent; /** * @@ -48,23 +49,29 @@ public abstract class StateTriggeredAbility extends TriggeredAbilityImpl { } @Override - public void trigger(Game game, UUID controllerId) { + public final boolean checkEventType(GameEvent event, Game game) { //20100716 - 603.8 - Boolean triggered = (Boolean) game.getState().getValue(this.getSourceId().toString() + "triggered"); + Boolean triggered = (Boolean) game.getState().getValue(getSourceId().toString() + "triggered"); if (triggered == null) { triggered = Boolean.FALSE; } - if (!triggered) { - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); - super.trigger(game, controllerId); - } + return !triggered; + } + + + @Override + public void trigger(Game game, UUID controllerId) { + //20100716 - 603.8 + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); + super.trigger(game, controllerId); } @Override public boolean resolve(Game game) { //20100716 - 603.8 + boolean result = super.resolve(game); game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); - return super.resolve(game); + return result; } public void counter(Game game) { diff --git a/Mage/src/mage/abilities/common/BecomesTargetTriggeredAbility.java b/Mage/src/mage/abilities/common/BecomesTargetTriggeredAbility.java index 12e9e3db26c..c80efba2104 100644 --- a/Mage/src/mage/abilities/common/BecomesTargetTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/BecomesTargetTriggeredAbility.java @@ -32,7 +32,6 @@ import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; /** * @@ -60,7 +59,7 @@ public class BecomesTargetTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - return event.getTargetId().equals(sourceId); + return event.getTargetId().equals(getSourceId()); } @Override diff --git a/Mage/src/mage/abilities/common/EntersBattlefieldTriggeredAbility.java b/Mage/src/mage/abilities/common/EntersBattlefieldTriggeredAbility.java index f34efeadc23..ddaf6be2b8a 100644 --- a/Mage/src/mage/abilities/common/EntersBattlefieldTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/EntersBattlefieldTriggeredAbility.java @@ -81,7 +81,7 @@ public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl { if (noRule) { return super.getRule(); } - return new StringBuilder(rulePrefix != null ? rulePrefix : "").append("When {this} enters the battlefield, ").append(super.getRule()).toString(); + return (rulePrefix != null ? rulePrefix : "") + "When {this} enters the battlefield, "+ super.getRule(); } @Override diff --git a/Mage/src/mage/abilities/common/LandfallAbility.java b/Mage/src/mage/abilities/common/LandfallAbility.java index 974593e5461..db19efb0ffd 100644 --- a/Mage/src/mage/abilities/common/LandfallAbility.java +++ b/Mage/src/mage/abilities/common/LandfallAbility.java @@ -34,7 +34,6 @@ import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; /** @@ -44,10 +43,10 @@ import mage.game.permanent.Permanent; public class LandfallAbility extends TriggeredAbilityImpl { public LandfallAbility(Effect effect, boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); + this(Zone.BATTLEFIELD, effect, optional); } - public LandfallAbility ( Zone zone, Effect effect, Boolean optional ) { + public LandfallAbility (Zone zone, Effect effect, Boolean optional ) { super(zone, effect, optional); } @@ -63,15 +62,12 @@ public class LandfallAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.getCardType().contains(CardType.LAND) && permanent.getControllerId().equals(this.controllerId)) { - return true; - } - return false; + return permanent != null && permanent.getCardType().contains(CardType.LAND) && permanent.getControllerId().equals(this.controllerId); } @Override public String getRule() { - return "Landfall - Whenever a land enters the battlefield under your control, " + super.getRule(); + return "Landfall — Whenever a land enters the battlefield under your control, " + super.getRule(); } @Override diff --git a/Mage/src/mage/abilities/common/PutIntoGraveFromBattlefieldAllTriggeredAbility.java b/Mage/src/mage/abilities/common/PutIntoGraveFromBattlefieldAllTriggeredAbility.java index be6495f8c90..45b41aec3ef 100644 --- a/Mage/src/mage/abilities/common/PutIntoGraveFromBattlefieldAllTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/PutIntoGraveFromBattlefieldAllTriggeredAbility.java @@ -54,12 +54,14 @@ public class PutIntoGraveFromBattlefieldAllTriggeredAbility extends TriggeredAbi super(Zone.BATTLEFIELD, effect, optional); this.filter = filter; this.onlyToControllerGraveyard = onlyToControllerGraveyard; + this.setTargetPointer = setTargetPointer; } public PutIntoGraveFromBattlefieldAllTriggeredAbility(final PutIntoGraveFromBattlefieldAllTriggeredAbility ability) { super(ability); this.filter = ability.filter; this.onlyToControllerGraveyard = ability.onlyToControllerGraveyard; + this.setTargetPointer = ability.setTargetPointer; } @Override diff --git a/Mage/src/mage/abilities/condition/common/LandfallCondition.java b/Mage/src/mage/abilities/condition/common/LandfallCondition.java index ecfa892b968..490e2b157c5 100644 --- a/Mage/src/mage/abilities/condition/common/LandfallCondition.java +++ b/Mage/src/mage/abilities/condition/common/LandfallCondition.java @@ -3,13 +3,13 @@ package mage.abilities.condition.common; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.game.Game; -import mage.watchers.Watcher; +import mage.watchers.common.LandfallWatcher; /** * @author Loki */ public class LandfallCondition implements Condition { - private static LandfallCondition instance = new LandfallCondition(); + private final static LandfallCondition instance = new LandfallCondition(); public static LandfallCondition getInstance() { return instance; @@ -20,7 +20,7 @@ public class LandfallCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - Watcher watcher = game.getState().getWatchers().get("LandPlayed", source.getControllerId()); - return watcher.conditionMet(); + LandfallWatcher watcher = (LandfallWatcher) game.getState().getWatchers().get("LandPlayed"); + return watcher != null && watcher.landPlayed(source.getControllerId()); } } diff --git a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java index 589f477d89b..28526853e3b 100644 --- a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java +++ b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java @@ -123,7 +123,7 @@ public class RemoveCounterCost extends CostImpl { int numberOfCountersSelected = 1; if (countersLeft > 1 && countersOnPermanent > 1) { numberOfCountersSelected = controller.getAmount(1, Math.min(countersLeft, countersOnPermanent), - new StringBuilder("Remove how many counters from ").append(permanent.getLogName()).toString(), game); + new StringBuilder("Remove how many counters from ").append(permanent.getIdName()).toString(), game); } permanent.removeCounters(counterName, numberOfCountersSelected, game); if (permanent.getCounters().getCount(counterName) == 0 ){ diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 0f76f9b5724..0b2c1fbe43b 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -390,7 +390,16 @@ public class ContinuousEffects implements Serializable { } private boolean checkAbilityStillExists(Ability ability, ContinuousEffect effect, GameEvent event, Game game) { - if (effect.getDuration().equals(Duration.OneUse) || effect.getDuration().equals(Duration.Custom) || ability.getSourceId() == null) { // needed for some special replacment effects (e.g. Undying) or commander replacement effect + switch(effect.getDuration()) { // effects with fixed duration don't need an object with the source ability (e.g. a silence cast with isochronic Scepter has no more a card object + case EndOfCombat: + case EndOfGame: + case EndOfStep: + case EndOfTurn: + case OneUse: + case Custom: // custom duration means the effect ends itself if needed + return true; + } + if (ability.getSourceId() == null) { // commander replacement effect return true; } MageObject object; @@ -794,7 +803,7 @@ public class ContinuousEffects implements Serializable { } } // Must be called here for some effects to be able to work correctly - // TODO: add info which effects that need + // TODO: add info which effects need that call game.applyEffects(); } while (true); return caught; diff --git a/Mage/src/mage/abilities/effects/common/AddManaOfAnyTypeProducedEffect.java b/Mage/src/mage/abilities/effects/common/AddManaOfAnyTypeProducedEffect.java index d07dd9f6f95..c230ee146f5 100644 --- a/Mage/src/mage/abilities/effects/common/AddManaOfAnyTypeProducedEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddManaOfAnyTypeProducedEffect.java @@ -54,7 +54,7 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(this.targetPointer.getFirst(game, source)); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent != null) { Player targetController = game.getPlayer(permanent.getControllerId()); if (targetController == null) { diff --git a/Mage/src/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java b/Mage/src/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java index ee08b2d7417..265b66e9cdd 100644 --- a/Mage/src/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java @@ -72,6 +72,7 @@ public class ChooseNewTargetsTargetEffect extends OneShotEffect { super(effect); this.forceChange = effect.forceChange; this.onlyOneTarget = effect.onlyOneTarget; + this.filterNewTarget = effect.filterNewTarget; } @Override diff --git a/Mage/src/mage/abilities/effects/common/CopyEffect.java b/Mage/src/mage/abilities/effects/common/CopyEffect.java index c9eeb76f353..bbb1d6ba0ba 100644 --- a/Mage/src/mage/abilities/effects/common/CopyEffect.java +++ b/Mage/src/mage/abilities/effects/common/CopyEffect.java @@ -97,7 +97,7 @@ public class CopyEffect extends ContinuousEffectImpl { return false; } permanent.setName(target.getName()); - permanent.getColor().setColor(target.getColor()); + permanent.getColor(game).setColor(target.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(target.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage/src/mage/abilities/effects/common/CopyTokenEffect.java b/Mage/src/mage/abilities/effects/common/CopyTokenEffect.java index 24b30457dda..c614624f4b9 100644 --- a/Mage/src/mage/abilities/effects/common/CopyTokenEffect.java +++ b/Mage/src/mage/abilities/effects/common/CopyTokenEffect.java @@ -29,7 +29,7 @@ public class CopyTokenEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); permanent.setName(token.getName()); - permanent.getColor().setColor(token.getColor()); + permanent.getColor(game).setColor(token.getColor(game)); permanent.getCardType().clear(); for (CardType type: token.getCardType()) { permanent.getCardType().add(type); diff --git a/Mage/src/mage/abilities/effects/common/ExileGraveyardAllPlayersEffect.java b/Mage/src/mage/abilities/effects/common/ExileGraveyardAllPlayersEffect.java new file mode 100644 index 00000000000..d5b8e67c35a --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/ExileGraveyardAllPlayersEffect.java @@ -0,0 +1,74 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.effects.common; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author Jgod + */ +public class ExileGraveyardAllPlayersEffect extends OneShotEffect { + public ExileGraveyardAllPlayersEffect() { + super(Outcome.Detriment); + staticText = "exile all cards from all graveyards"; + } + + @Override + public ExileGraveyardAllPlayersEffect copy() { + return new ExileGraveyardAllPlayersEffect(); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + + for (UUID playerId : controller.getInRange()) { + Player player = game.getPlayer(playerId); + if (player != null) { + for (UUID cid : player.getGraveyard().copy()) { + Card card = game.getCard(cid); + if (card != null) { + controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.GRAVEYARD, true); + } + } + } + } + return true; + } +} \ No newline at end of file diff --git a/Mage/src/mage/abilities/effects/common/PreventDamageBySourceEffect.java b/Mage/src/mage/abilities/effects/common/PreventDamageBySourceEffect.java index da4b7082b3c..6090fcc2371 100644 --- a/Mage/src/mage/abilities/effects/common/PreventDamageBySourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/PreventDamageBySourceEffect.java @@ -54,12 +54,11 @@ public class PreventDamageBySourceEffect extends PreventionEffectImpl { public PreventDamageBySourceEffect(FilterObject filterObject) { super(Duration.EndOfTurn); - if (filterObject.getMessage().equals("a")) { - this.target = new TargetSource(new FilterObject("source")); - } else { - this.target = new TargetSource(new FilterObject(filterObject.getMessage() + " source")); + if (!filterObject.getMessage().endsWith("source")) { + filterObject.setMessage(filterObject.getMessage() + " source"); } - staticText = "Prevent all damage " + filterObject.getMessage() + " source of your choice would deal this turn"; + this.target = new TargetSource(filterObject); + staticText = "Prevent all damage " + filterObject.getMessage() + " of your choice would deal this turn"; } public PreventDamageBySourceEffect(final PreventDamageBySourceEffect effect) { diff --git a/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java b/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java index d81b6a574cd..2efd33a0420 100644 --- a/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java @@ -82,6 +82,9 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect { @Override public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } StringBuilder sb = new StringBuilder(); String message = amount.getMessage(); diff --git a/Mage/src/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java b/Mage/src/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java index c4e8b654eb9..e5cf0b29436 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java @@ -27,14 +27,13 @@ */ package mage.abilities.effects.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetControlledPermanent; import mage.util.CardUtil; @@ -44,13 +43,14 @@ import mage.util.CardUtil; * @author Plopmans */ public class ReturnToHandChosenControlledPermanentEffect extends OneShotEffect { - + private final FilterControlledPermanent filter; private int number; - + public ReturnToHandChosenControlledPermanentEffect(FilterControlledPermanent filter) { this(filter, 1); } + public ReturnToHandChosenControlledPermanentEffect(FilterControlledPermanent filter, int number) { super(Outcome.ReturnToHand); this.filter = filter; @@ -71,15 +71,13 @@ public class ReturnToHandChosenControlledPermanentEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - TargetControlledPermanent target = new TargetControlledPermanent(number, number, filter, true); - if (player.choose(this.outcome, target, source.getSourceId(), game)) { - for (UUID targetCreatureId : target.getTargets()) { - Permanent permanent = game.getPermanent(targetCreatureId); - if (permanent != null) { - player.moveCardToHandWithInfo(permanent, source.getSourceId(), game, Zone.BATTLEFIELD); - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int available = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); + if (available > 0) { + TargetControlledPermanent target = new TargetControlledPermanent(Math.min(number, available), number, filter, true); + if (controller.chooseTarget(this.outcome, target, source, game)) { + controller.moveCards(new CardsImpl(target.getTargets()), Zone.BATTLEFIELD, Zone.HAND, source, game); } } return true; diff --git a/Mage/src/mage/abilities/effects/common/SacrificeEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeEffect.java index e03acac3325..ad24e45819a 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeEffect.java @@ -90,7 +90,6 @@ public class SacrificeEffect extends OneShotEffect{ // A spell or ability could have removed the only legal target this player // had, if thats the case this ability should fizzle. if (amount > 0 && target.canChoose(source.getSourceId(), player.getId(), game)) { - boolean abilityApplied = false; while (!target.isChosen() && target.canChoose(player.getId(), game) && player.isInGame()) { player.chooseTarget(Outcome.Sacrifice, target, source, game); } @@ -99,11 +98,11 @@ public class SacrificeEffect extends OneShotEffect{ Permanent permanent = game.getPermanent(target.getTargets().get(idx)); if ( permanent != null ) { - abilityApplied |= permanent.sacrifice(source.getSourceId(), game); + permanent.sacrifice(source.getSourceId(), game); } } - return abilityApplied; + return true; } return false; } diff --git a/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java index 2320b261986..6edd6c83449 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java @@ -87,14 +87,11 @@ public class SacrificeOpponentsEffect extends OneShotEffect { filter.add(new ControllerPredicate(TargetController.YOU)); for (UUID playerId : game.getOpponents(source.getControllerId())) { Player player = game.getPlayer(playerId); - if (player != null) { - + if (player != null) { int numTargets = Math.min(amount.calculate(game, source, this), game.getBattlefield().countAll(filter, player.getId(), game)); - TargetPermanent target = new TargetPermanent(numTargets, numTargets, filter, false); + TargetPermanent target = new TargetPermanent(numTargets, numTargets, filter, true); if (target.canChoose(player.getId(), game)) { - while (!target.isChosen() && player.isInGame()) { - player.chooseTarget(Outcome.Sacrifice, target, source, game); - } + player.chooseTarget(Outcome.Sacrifice, target, source, game); perms.addAll(target.getTargets()); } } diff --git a/Mage/src/mage/abilities/effects/common/SacrificeSourceEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeSourceEffect.java index 0b78c64aeed..6d8727059a9 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeSourceEffect.java @@ -28,6 +28,7 @@ package mage.abilities.effects.common; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; @@ -56,13 +57,17 @@ public class SacrificeSourceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (sourceObject instanceof Permanent) { + Permanent permanent = (Permanent) sourceObject; // you can only sacrifice a permanent you control if (source.getControllerId().equals(permanent.getControllerId())) { return permanent.sacrifice(source.getSourceId(), game); } return true; + } else { + // no permanent? + sourceObject.getName(); } return false; } diff --git a/Mage/src/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java b/Mage/src/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java index a457e94d455..ba0fba94acd 100644 --- a/Mage/src/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java @@ -48,7 +48,7 @@ public class CanBlockOnlyFlyingAttachedEffect extends RestrictionEffect { if (attachmentType.equals(AttachmentType.AURA)) { this.staticText = "Enchanted creature can block only creatures with flying"; } else { - this.staticText = "Equiped creature can block only creatures with flying"; + this.staticText = "Equipped creature can block only creatures with flying"; } } diff --git a/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedAttachedEffect.java b/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedAttachedEffect.java index acf01412d8a..3d498d45856 100644 --- a/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedAttachedEffect.java @@ -45,7 +45,7 @@ public class CantBeBlockedAttachedEffect extends RestrictionEffect { if (attachmentType.equals(AttachmentType.AURA)) { this.staticText = "Enchanted creature can't be blocked"; } else { - this.staticText = "Equiped creature can't be blocked"; + this.staticText = "Equipped creature can't be blocked"; } } diff --git a/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAttachedEffect.java b/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAttachedEffect.java index 576062930fc..967ffdce1e1 100644 --- a/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAttachedEffect.java @@ -48,9 +48,9 @@ public class CantBeBlockedByCreaturesAttachedEffect extends RestrictionEffect { this.filter = filter; StringBuilder sb = new StringBuilder(); if (attachmentType.equals(AttachmentType.AURA)) { - sb.append("Enchanted"); + sb.append("Enchanted "); } else { - sb.append("Equipped"); + sb.append("Equipped "); } staticText = sb.append("creature can't be blocked ") .append(filter.getMessage().startsWith("except by") ? "":"by ").append(filter.getMessage()).toString(); diff --git a/Mage/src/mage/abilities/effects/common/continuous/AddCardColorAttachedEffect.java b/Mage/src/mage/abilities/effects/common/continuous/AddCardColorAttachedEffect.java index 93e1b523160..b81b21de6f6 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/AddCardColorAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/AddCardColorAttachedEffect.java @@ -63,15 +63,15 @@ public class AddCardColorAttachedEffect extends ContinuousEffectImpl { Permanent target = game.getPermanent(equipment.getAttachedTo()); if (target != null) { if (addedColor.isBlack()) - target.getColor().setBlack(true); + target.getColor(game).setBlack(true); if (addedColor.isBlue()) - target.getColor().setBlue(true); + target.getColor(game).setBlue(true); if (addedColor.isWhite()) - target.getColor().setWhite(true); + target.getColor(game).setWhite(true); if (addedColor.isGreen()) - target.getColor().setGreen(true); + target.getColor(game).setGreen(true); if (addedColor.isRed()) - target.getColor().setRed(true); + target.getColor(game).setRed(true); } } return true; diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java index 27c18a29b57..068fee171e2 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java @@ -85,11 +85,11 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { if (permanent != null) { switch (layer) { case ColorChangingEffects_5: - permanent.getColor().setWhite(false); - permanent.getColor().setGreen(false); - permanent.getColor().setBlack(false); - permanent.getColor().setBlue(false); - permanent.getColor().setRed(false); + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setGreen(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setRed(false); break; case AbilityAddingRemovingEffects_6: permanent.removeAllAbilities(source.getSourceId(), game); diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java index 6c74ef7e58c..975beaa6e71 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java @@ -41,8 +41,6 @@ import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; import mage.players.Player; /** @@ -50,7 +48,7 @@ import mage.players.Player; */ public class BecomesColorTargetEffect extends ContinuousEffectImpl { - private final ObjectColor setColor; + private ObjectColor setColor; /** * Set the color of a spell or permanent @@ -76,49 +74,55 @@ public class BecomesColorTargetEffect extends ContinuousEffectImpl { } @Override - public boolean apply(Game game, Ability source) { + public void init(Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); if (controller == null) { - return false; - } - boolean result = false; - ObjectColor objectColor; + return; + } if (setColor == null) { ChoiceColor choice = new ChoiceColor(); while (!choice.isChosen()) { controller.choose(Outcome.PutManaInPool, choice, game); if (!controller.isInGame()) { - return false; + return; } } if (choice.getColor() != null) { - objectColor = choice.getColor(); + setColor = choice.getColor(); } else { - return false; + return; } if (!game.isSimulation()) { - game.informPlayers(controller.getLogName() + " has chosen the color: " + objectColor.toString()); + game.informPlayers(controller.getLogName() + " has chosen the color: " + setColor.toString()); } - } else { - objectColor = this.setColor; + } + + + super.init(source, game); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; } - if (objectColor != null) { + if (setColor != null) { + boolean objectFound = false; for (UUID targetId :targetPointer.getTargets(game, source)) { - MageObject o = game.getObject(targetId); - if (o != null) { - if (o instanceof Permanent || o instanceof StackObject) { - o.getColor().setColor(objectColor); - result = true; - } + MageObject targetObject = game.getObject(targetId); + if (targetObject != null) { + objectFound = true; + targetObject.getColor(game).setColor(setColor); } } - } - if (!result) { - if (this.getDuration().equals(Duration.Custom)) { + if (!objectFound && this.getDuration().equals(Duration.Custom)) { this.discard(); } + return true; + } else { + throw new UnsupportedOperationException("No color set"); } - return result; } @Override diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java index 50802b7894e..f891657cff5 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java @@ -93,8 +93,8 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - if (token.getColor().hasColor()) - permanent.getColor().setColor(token.getColor()); + if (token.getColor(game).hasColor()) + permanent.getColor(game).setColor(token.getColor(game)); } break; case AbilityAddingRemovingEffects_6: diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java index 89f97a867d5..329ed1d78e4 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java @@ -115,14 +115,14 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl { case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { if (loseOther) { - permanent.getColor().setBlack(false); - permanent.getColor().setGreen(false); - permanent.getColor().setBlue(false); - permanent.getColor().setWhite(false); - permanent.getColor().setRed(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setGreen(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setRed(false); } - if (token.getColor().hasColor()) { - permanent.getColor().setColor(token.getColor()); + if (token.getColor(game).hasColor()) { + permanent.getColor(game).setColor(token.getColor(game)); } } break; diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java index ee60cdf57eb..927f8974466 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java @@ -105,8 +105,8 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - if (token.getColor().hasColor()) { - permanent.getColor().setColor(token.getColor()); + if (token.getColor(game).hasColor()) { + permanent.getColor(game).setColor(token.getColor(game)); } } break; diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java index 8bee7f94b8f..10c22909b9d 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java @@ -111,14 +111,14 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl { case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { if (loseAllAbilities) { - permanent.getColor().setBlack(false); - permanent.getColor().setGreen(false); - permanent.getColor().setBlue(false); - permanent.getColor().setWhite(false); - permanent.getColor().setBlack(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setGreen(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setBlack(false); } - if (token.getColor().hasColor()) { - permanent.getColor().setColor(token.getColor()); + if (token.getColor(game).hasColor()) { + permanent.getColor(game).setColor(token.getColor(game)); } } break; diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java index 3896cd97f6e..3abed713076 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java @@ -118,7 +118,7 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple permanent.getManaCost().clear(); break; case ColorChangingEffects_5: - permanent.getColor().setColor(new ObjectColor()); + permanent.getColor(game).setColor(new ObjectColor()); break; case AbilityAddingRemovingEffects_6: Card card = game.getCard(permanent.getId()); // diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java index 14bb52a456b..cd0831f27df 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java @@ -175,7 +175,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen permanent.getSubtype().clear(); break; case ColorChangingEffects_5: - permanent.getColor().setColor(new ObjectColor()); + permanent.getColor(game).setColor(new ObjectColor()); break; case AbilityAddingRemovingEffects_6: Card card = game.getCard(permanent.getId()); // diff --git a/Mage/src/mage/abilities/effects/common/continuous/GainProtectionFromColorTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/GainProtectionFromColorTargetEffect.java index 1767fb43c20..a6af9b8dcc6 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/GainProtectionFromColorTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/GainProtectionFromColorTargetEffect.java @@ -51,7 +51,7 @@ public class GainProtectionFromColorTargetEffect extends GainAbilityTargetEffect public GainProtectionFromColorTargetEffect(Duration duration) { super(new ProtectionAbility(new FilterCard()), duration); - choice = new ChoiceColor(); + choice = new ChoiceColor(true); } public GainProtectionFromColorTargetEffect(final GainProtectionFromColorTargetEffect effect) { diff --git a/Mage/src/mage/abilities/effects/common/continuous/SetCardColorAttachedEffect.java b/Mage/src/mage/abilities/effects/common/continuous/SetCardColorAttachedEffect.java index 9cdf7026418..dd32ebda2fb 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/SetCardColorAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/SetCardColorAttachedEffect.java @@ -61,7 +61,7 @@ public class SetCardColorAttachedEffect extends ContinuousEffectImpl { if (equipment != null && equipment.getAttachedTo() != null) { Permanent target = game.getPermanent(equipment.getAttachedTo()); if (target != null) { - target.getColor().setColor(setColor); + target.getColor(game).setColor(setColor); return true; } } diff --git a/Mage/src/mage/abilities/effects/common/continuous/SetCardColorSourceEffect.java b/Mage/src/mage/abilities/effects/common/continuous/SetCardColorSourceEffect.java index 85340707a5b..e45ed9f9a42 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/SetCardColorSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/SetCardColorSourceEffect.java @@ -75,7 +75,7 @@ public class SetCardColorSourceEffect extends ContinuousEffectImpl { MageObject o = game.getObject(source.getSourceId()); if (o != null) { if (o instanceof Permanent || o instanceof StackObject) { - o.getColor().setColor(setColor); + o.getColor(game).setColor(setColor); } } diff --git a/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutOnLibraryEffect.java b/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutOnLibraryEffect.java index 33e6558d8e1..65e05e65846 100644 --- a/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutOnLibraryEffect.java +++ b/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutOnLibraryEffect.java @@ -78,22 +78,22 @@ public class SearchLibraryPutOnLibraryEffect extends SearchEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); + MageObject sourceObject = source.getSourceObject(game); if (controller == null || sourceObject == null) { return false; } if (controller.searchLibrary(target, game)) { List cards = new ArrayList<>(); - for (UUID cardId: (List)target.getTargets()) { + for (UUID cardId: target.getTargets()) { Card card = controller.getLibrary().remove(cardId, game); if (card != null) { cards.add(card); } } Cards foundCards = new CardsImpl(); - foundCards.addAll(cards); + foundCards.addAll(target.getTargets()); if (reveal) { - controller.revealCards(sourceObject.getName(), foundCards, game); + controller.revealCards(sourceObject.getIdName(), foundCards, game); } if (forceShuffle) { controller.shuffleLibrary(game); @@ -117,7 +117,7 @@ public class SearchLibraryPutOnLibraryEffect extends SearchEffect { StringBuilder sb = new StringBuilder(); sb.append("Search your library for a ").append(target.getTargetName()); if (reveal) { - sb.append("and reveal that card. Shuffle"); + sb.append(" and reveal that card. Shuffle"); } else { sb.append(", then shuffle"); } diff --git a/Mage/src/mage/abilities/keyword/AffinityForArtifactsAbility.java b/Mage/src/mage/abilities/keyword/AffinityForArtifactsAbility.java index 6bb24081cb3..2f2a1729280 100644 --- a/Mage/src/mage/abilities/keyword/AffinityForArtifactsAbility.java +++ b/Mage/src/mage/abilities/keyword/AffinityForArtifactsAbility.java @@ -65,7 +65,7 @@ public class AffinityForArtifactsAbility extends SimpleStaticAbility implements @Override public String getRule() { - return "Affinity for artifacts"; + return "affinity for artifacts (This spell costs {1} less to cast for each artifact you control.)"; } @Override diff --git a/Mage/src/mage/abilities/keyword/AffinityForLandTypeAbility.java b/Mage/src/mage/abilities/keyword/AffinityForLandTypeAbility.java index 451aac0c7df..a0fadfd3787 100644 --- a/Mage/src/mage/abilities/keyword/AffinityForLandTypeAbility.java +++ b/Mage/src/mage/abilities/keyword/AffinityForLandTypeAbility.java @@ -32,10 +32,8 @@ import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.AdjustingSourceCosts; import mage.abilities.effects.common.AffinityEffect; -import mage.constants.CardType; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.Game; import mage.util.CardUtil; @@ -50,12 +48,14 @@ public class AffinityForLandTypeAbility extends SimpleStaticAbility implements A private final FilterControlledPermanent filter; String text; + String landType; public AffinityForLandTypeAbility(String landType, String text) { super(Zone.OUTSIDE, new AffinityEffect(getFilter(landType))); this.filter = getFilter(landType); setRuleAtTheTop(true); this.text = text; + this.landType = landType; } private static FilterControlledPermanent getFilter(String landType) { @@ -78,7 +78,7 @@ public class AffinityForLandTypeAbility extends SimpleStaticAbility implements A @Override public String getRule() { - return "Affinity for " + text; + return "Affinity for " + text + " (This spell costs 1 less to cast for each " + landType + " you control.)"; } @Override @@ -90,4 +90,4 @@ public class AffinityForLandTypeAbility extends SimpleStaticAbility implements A } } } -} \ No newline at end of file +} diff --git a/Mage/src/mage/abilities/keyword/ConvokeAbility.java b/Mage/src/mage/abilities/keyword/ConvokeAbility.java index 9449efcb458..ef920d57d42 100644 --- a/Mage/src/mage/abilities/keyword/ConvokeAbility.java +++ b/Mage/src/mage/abilities/keyword/ConvokeAbility.java @@ -207,7 +207,7 @@ class ConvokeEffect extends OneShotEffect { String manaName; if (!perm.isTapped() && perm.tap(game)) { ManaPool manaPool = controller.getManaPool(); - Choice chooseManaType = buildChoice(perm.getColor(), unpaid.getMana()); + Choice chooseManaType = buildChoice(perm.getColor(game), unpaid.getMana()); if (chooseManaType.getChoices().size() > 0) { if (chooseManaType.getChoices().size() > 1) { chooseManaType.getChoices().add("Colorless"); diff --git a/Mage/src/mage/abilities/keyword/CyclingAbility.java b/Mage/src/mage/abilities/keyword/CyclingAbility.java index 27dcf0418ab..e52a77724c8 100644 --- a/Mage/src/mage/abilities/keyword/CyclingAbility.java +++ b/Mage/src/mage/abilities/keyword/CyclingAbility.java @@ -32,7 +32,7 @@ import mage.constants.Zone; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.costs.Cost; import mage.abilities.costs.common.DiscardSourceCost; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCost; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.filter.FilterCard; @@ -76,11 +76,11 @@ public class CyclingAbility extends ActivatedAbilityImpl { @Override public String getRule() { StringBuilder rule = new StringBuilder(this.text); - if(cost instanceof ManaCosts){ + if(cost instanceof ManaCost){ rule.append(" "); } else{ - rule.append(" - "); + rule.append("—"); } rule.append(cost.getText()).append(" (").append(super.getRule(true)).append(")"); return rule.toString(); diff --git a/Mage/src/mage/abilities/keyword/DashAbility.java b/Mage/src/mage/abilities/keyword/DashAbility.java index dbb7ddbcbe4..a80b5f50796 100644 --- a/Mage/src/mage/abilities/keyword/DashAbility.java +++ b/Mage/src/mage/abilities/keyword/DashAbility.java @@ -77,6 +77,7 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.Custom, false), DashedCondition.getInstance(), false, "",""); Effect effect = new ReturnToHandTargetEffect(); + effect.setText("return the dashed creature from the battlefield to its owner's hand"); effect.setTargetPointer(new FixedTarget(card.getId())); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), false)); addSubAbility(ability); diff --git a/Mage/src/mage/abilities/keyword/FearAbility.java b/Mage/src/mage/abilities/keyword/FearAbility.java index a860cfd25aa..9324251eb86 100644 --- a/Mage/src/mage/abilities/keyword/FearAbility.java +++ b/Mage/src/mage/abilities/keyword/FearAbility.java @@ -91,7 +91,7 @@ class FearEffect extends RestrictionEffect implements MageSingleton { @Override public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game) { - if (blocker.getCardType().contains(CardType.ARTIFACT) || blocker.getColor().isBlack()) { + if (blocker.getCardType().contains(CardType.ARTIFACT) || blocker.getColor(game).isBlack()) { return true; } return false; diff --git a/Mage/src/mage/abilities/keyword/HeroicAbility.java b/Mage/src/mage/abilities/keyword/HeroicAbility.java index e7501685363..5b89f5d65b1 100644 --- a/Mage/src/mage/abilities/keyword/HeroicAbility.java +++ b/Mage/src/mage/abilities/keyword/HeroicAbility.java @@ -83,17 +83,19 @@ public class HeroicAbility extends TriggeredAbilityImpl { private boolean checkSpell(Spell spell, Game game) { if (spell != null) { SpellAbility sa = spell.getSpellAbility(); - for (Target target : sa.getTargets()) { - if (!target.isNotTarget() && target.getTargets().contains(this.getSourceId())) { - return true; - } - } - for (Effect effect : sa.getEffects()) { - for (UUID targetId : effect.getTargetPointer().getTargets(game, sa)) { - if (targetId.equals(this.getSourceId())) { + for(UUID modeId :sa.getModes().getSelectedModes()) { + for (Target target : sa.getModes().get(modeId).getTargets()) { + if (!target.isNotTarget() && target.getTargets().contains(this.getSourceId())) { return true; } } + for (Effect effect : sa.getModes().get(modeId).getEffects()) { + for (UUID targetId : effect.getTargetPointer().getTargets(game, sa)) { + if (targetId.equals(this.getSourceId())) { + return true; + } + } + } } } return false; diff --git a/Mage/src/mage/abilities/keyword/IntimidateAbility.java b/Mage/src/mage/abilities/keyword/IntimidateAbility.java index c48b166f428..01e8b02bc56 100644 --- a/Mage/src/mage/abilities/keyword/IntimidateAbility.java +++ b/Mage/src/mage/abilities/keyword/IntimidateAbility.java @@ -64,7 +64,7 @@ class IntimidateEffect extends RestrictionEffect implements MageSingleton { if (blocker.getCardType().contains(CardType.ARTIFACT) && (blocker.getCardType().contains(CardType.CREATURE))) { result = true; } - if (attacker.getColor().shares(blocker.getColor())) { + if (attacker.getColor(game).shares(blocker.getColor(game))) { result = true; } return result; diff --git a/Mage/src/mage/abilities/keyword/MorphAbility.java b/Mage/src/mage/abilities/keyword/MorphAbility.java index d6ec2347383..362be6c6191 100644 --- a/Mage/src/mage/abilities/keyword/MorphAbility.java +++ b/Mage/src/mage/abilities/keyword/MorphAbility.java @@ -219,7 +219,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost } } // change spell colors - ObjectColor spellColor = spell.getColor(); + ObjectColor spellColor = spell.getColor(game); spellColor.setBlack(false); spellColor.setRed(false); spellColor.setGreen(false); @@ -297,7 +297,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost mageObject.getPower().initValue(2); mageObject.getToughness().initValue(2); mageObject.getAbilities().clear(); - mageObject.getColor().setColor(new ObjectColor()); + mageObject.getColor(null).setColor(new ObjectColor()); mageObject.setName(""); mageObject.getCardType().clear(); mageObject.getCardType().add(CardType.CREATURE); diff --git a/Mage/src/mage/abilities/keyword/PersistAbility.java b/Mage/src/mage/abilities/keyword/PersistAbility.java index 172808456a6..2577fec20eb 100644 --- a/Mage/src/mage/abilities/keyword/PersistAbility.java +++ b/Mage/src/mage/abilities/keyword/PersistAbility.java @@ -35,10 +35,11 @@ import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -68,9 +69,11 @@ public class PersistAbility extends DiesTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { if (super.checkTrigger(event, game)) { - Permanent p = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); - if (p.getCounters().getCount(CounterType.M1M1) == 0) { - game.getState().setValue("persist" + getSourceId().toString(), new FixedTarget(p.getId())); + Permanent permanent = ((ZoneChangeEvent) event).getTarget(); + if (permanent.getCounters().getCount(CounterType.M1M1) == 0) { + FixedTarget fixedTarget = new FixedTarget(permanent.getId()); + fixedTarget.init(game, this); + game.getState().setValue("persist" + getSourceId().toString(), fixedTarget); return true; } } @@ -109,7 +112,7 @@ class PersistEffect extends OneShotEffect { class PersistReplacementEffect extends ReplacementEffectImpl { PersistReplacementEffect() { - super(Duration.OneUse, Outcome.UnboostCreature, false); + super(Duration.Custom, Outcome.UnboostCreature, false); selfScope = true; staticText = "return it to the battlefield under its owner's control with a -1/-1 counter on it"; } @@ -129,7 +132,7 @@ class PersistReplacementEffect extends ReplacementEffectImpl { if (permanent != null) { permanent.addCounters(CounterType.M1M1.createInstance(), game); } - used = true; + discard(); return false; } @@ -142,7 +145,9 @@ class PersistReplacementEffect extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getTargetId().equals(source.getSourceId())) { Object fixedTarget = game.getState().getValue("persist" + source.getSourceId().toString()); - if (fixedTarget instanceof FixedTarget && ((FixedTarget) fixedTarget).getFirst(game, source).equals(source.getSourceId())) { + if (fixedTarget instanceof FixedTarget && ((FixedTarget) fixedTarget).getTarget().equals(source.getSourceId()) && + ((FixedTarget) fixedTarget).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(source.getSourceId())) { + return true; } } diff --git a/Mage/src/mage/abilities/keyword/TransformAbility.java b/Mage/src/mage/abilities/keyword/TransformAbility.java index 55a97861b70..9b01d319c02 100644 --- a/Mage/src/mage/abilities/keyword/TransformAbility.java +++ b/Mage/src/mage/abilities/keyword/TransformAbility.java @@ -69,7 +69,7 @@ public class TransformAbility extends SimpleStaticAbility { } permanent.setName(sourceCard.getName()); - permanent.getColor().setColor(sourceCard.getColor()); + permanent.getColor(game).setColor(sourceCard.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(sourceCard.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage/src/mage/cards/Card.java b/Mage/src/mage/cards/Card.java index f89865d773b..91217b0c02a 100644 --- a/Mage/src/mage/cards/Card.java +++ b/Mage/src/mage/cards/Card.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.UUID; import mage.MageObject; import mage.Mana; +import mage.ObjectColor; import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.SpellAbility; @@ -134,5 +135,5 @@ public interface Card extends MageObject { */ Card getMainCard(); - void setZone(Zone zone, Game game); + void setZone(Zone zone, Game game); } diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java index 9458a22fe17..b656ae3bf29 100644 --- a/Mage/src/mage/cards/CardImpl.java +++ b/Mage/src/mage/cards/CardImpl.java @@ -36,6 +36,7 @@ import java.util.UUID; import mage.MageObject; import mage.MageObjectImpl; import mage.Mana; +import mage.ObjectColor; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; @@ -59,6 +60,7 @@ import static mage.constants.Zone.PICK; import static mage.constants.Zone.STACK; import mage.counters.Counter; import mage.counters.Counters; +import mage.game.CardAttribute; import mage.game.CardState; import mage.game.Game; import mage.game.command.Commander; @@ -763,5 +765,15 @@ public abstract class CardImpl extends MageObjectImpl implements Card { public void setSpellAbility(SpellAbility ability) { spellAbility = ability; } - + + @Override + public ObjectColor getColor(Game game) { + if (game != null) { + CardAttribute cardAttribute = game.getState().getCardAttribute(getId()); + if (cardAttribute != null) { + return cardAttribute.getColor(); + } + } + return super.getColor(game); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/Mage/src/mage/cards/repository/CardInfo.java b/Mage/src/mage/cards/repository/CardInfo.java index 4b1c4905f13..901ab028540 100644 --- a/Mage/src/mage/cards/repository/CardInfo.java +++ b/Mage/src/mage/cards/repository/CardInfo.java @@ -132,11 +132,11 @@ public class CardInfo { this.secondSideName = secondSide.getName(); } - this.blue = card.getColor().isBlue(); - this.black = card.getColor().isBlack(); - this.green = card.getColor().isGreen(); - this.red = card.getColor().isRed(); - this.white = card.getColor().isWhite(); + this.blue = card.getColor(null).isBlue(); + this.black = card.getColor(null).isBlack(); + this.green = card.getColor(null).isGreen(); + this.red = card.getColor(null).isRed(); + this.white = card.getColor(null).isWhite(); this.setTypes(card.getCardType()); this.setSubtypes(card.getSubtype()); diff --git a/Mage/src/mage/constants/PlayerAction.java b/Mage/src/mage/constants/PlayerAction.java index ccf54950b4b..82c18616d72 100644 --- a/Mage/src/mage/constants/PlayerAction.java +++ b/Mage/src/mage/constants/PlayerAction.java @@ -39,14 +39,20 @@ public enum PlayerAction { PASS_PRIORITY_UNTIL_NEXT_TURN, PASS_PRIORITY_UNTIL_STACK_RESOLVED, PASS_PRIORITY_CANCEL_ALL_ACTIONS, + ROLLBACK_TURNS, UNDO, CONCEDE, MANA_AUTO_PAYMENT_ON, MANA_AUTO_PAYMENT_OFF, + MANA_AUTO_PAYMENT_RESTRICTED_ON, + MANA_AUTO_PAYMENT_RESTRICTED_OFF, RESET_AUTO_SELECT_REPLACEMENT_EFFECTS, REVOKE_PERMISSIONS_TO_SEE_HAND_CARDS, REQUEST_PERMISSION_TO_SEE_HAND_CARDS, + REQUEST_PERMISSION_TO_ROLLBACK_TURN, ADD_PERMISSION_TO_SEE_HAND_CARDS, + ADD_PERMISSION_TO_ROLLBACK_TURN, + DENY_PERMISSON_TO_ROLLBACK_TURN, PERMISSION_REQUESTS_ALLOWED_ON, PERMISSION_REQUESTS_ALLOWED_OFF } \ No newline at end of file diff --git a/Mage/src/mage/filter/common/FilterArtifactOrEnchantmentPermanent.java b/Mage/src/mage/filter/common/FilterArtifactOrEnchantmentPermanent.java index b65da88c931..7aabd0eeec5 100644 --- a/Mage/src/mage/filter/common/FilterArtifactOrEnchantmentPermanent.java +++ b/Mage/src/mage/filter/common/FilterArtifactOrEnchantmentPermanent.java @@ -1,9 +1,30 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * 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.filter.common; import mage.constants.CardType; @@ -15,7 +36,6 @@ import mage.filter.predicate.mageobject.CardTypePredicate; * * @author LevelX2 */ - public class FilterArtifactOrEnchantmentPermanent extends FilterPermanent { public FilterArtifactOrEnchantmentPermanent() { diff --git a/Mage/src/mage/filter/common/FilterArtifactSpell.java b/Mage/src/mage/filter/common/FilterArtifactSpell.java new file mode 100644 index 00000000000..530c21d6a69 --- /dev/null +++ b/Mage/src/mage/filter/common/FilterArtifactSpell.java @@ -0,0 +1,48 @@ +/* + * 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.filter.common; + +import mage.constants.CardType; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.CardTypePredicate; + +/** + * + * @author Jgod + */ +public class FilterArtifactSpell extends FilterSpell { + + public FilterArtifactSpell() { + this("artifact spell"); + } + + public FilterArtifactSpell(String name) { + super(name); + this.add(new CardTypePredicate(CardType.ARTIFACT)); + } +} diff --git a/Mage/src/mage/filter/predicate/mageobject/AbilityPredicate.java b/Mage/src/mage/filter/predicate/mageobject/AbilityPredicate.java index 1deeefdd75a..42489015ab4 100644 --- a/Mage/src/mage/filter/predicate/mageobject/AbilityPredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/AbilityPredicate.java @@ -30,6 +30,7 @@ package mage.filter.predicate.mageobject; import mage.MageObject; import mage.abilities.Abilities; import mage.abilities.Ability; +import mage.cards.Card; import mage.filter.predicate.Predicate; import mage.game.Game; @@ -47,9 +48,15 @@ public class AbilityPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { - Abilities abilities = input.getAbilities(); - for (int i = 0; i < abilities.size(); i++) { - if (abilityClass.equals(abilities.get(i).getClass())) { + Abilities abilities; + if (input instanceof Card){ + abilities = ((Card)input).getAbilities(game); + } else { + abilities = input.getAbilities(); + } + + for (Ability ability : abilities) { + if (abilityClass.equals(ability.getClass())) { return true; } } diff --git a/Mage/src/mage/filter/predicate/mageobject/ColorPredicate.java b/Mage/src/mage/filter/predicate/mageobject/ColorPredicate.java index 0eba7899aff..74e5164049d 100644 --- a/Mage/src/mage/filter/predicate/mageobject/ColorPredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/ColorPredicate.java @@ -46,7 +46,7 @@ public class ColorPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { - return input.getColor().contains(color); + return input.getColor(game).contains(color); } @Override diff --git a/Mage/src/mage/filter/predicate/mageobject/ColorlessPredicate.java b/Mage/src/mage/filter/predicate/mageobject/ColorlessPredicate.java index 8a7718bf386..fa6b88d4bcd 100644 --- a/Mage/src/mage/filter/predicate/mageobject/ColorlessPredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/ColorlessPredicate.java @@ -39,7 +39,7 @@ public class ColorlessPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { - return input.getColor().isColorless(); + return input.getColor(game).isColorless(); } @Override diff --git a/Mage/src/mage/filter/predicate/mageobject/MonocoloredPredicate.java b/Mage/src/mage/filter/predicate/mageobject/MonocoloredPredicate.java index f6bd95ba51c..dbb0ba54834 100644 --- a/Mage/src/mage/filter/predicate/mageobject/MonocoloredPredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/MonocoloredPredicate.java @@ -39,7 +39,7 @@ public class MonocoloredPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { - return 1 == input.getColor().getColorCount(); + return 1 == input.getColor(game).getColorCount(); } @Override diff --git a/Mage/src/mage/filter/predicate/mageobject/MulticoloredPredicate.java b/Mage/src/mage/filter/predicate/mageobject/MulticoloredPredicate.java index 196ad51753a..6aca6494c25 100644 --- a/Mage/src/mage/filter/predicate/mageobject/MulticoloredPredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/MulticoloredPredicate.java @@ -39,7 +39,7 @@ public class MulticoloredPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { - return 1 < input.getColor().getColorCount(); + return 1 < input.getColor(game).getColorCount(); } @Override diff --git a/Mage/src/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java b/Mage/src/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java index caf9ae4306a..8633e960971 100644 --- a/Mage/src/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java @@ -22,7 +22,7 @@ public class SharesColorWithSourcePredicate implements ObjectSourcePlayerPredica public boolean apply(ObjectSourcePlayer input, Game game) { MageObject sourceObject = game.getObject(input.getSourceId()); if (sourceObject != null) { - return input.getObject().getColor().shares(sourceObject.getColor()); + return input.getObject().getColor(game).shares(sourceObject.getColor(game)); } return false; diff --git a/Mage/src/mage/filter/predicate/permanent/UnblockedPredicate.java b/Mage/src/mage/filter/predicate/permanent/UnblockedPredicate.java index 46cbb07245d..b5667793083 100644 --- a/Mage/src/mage/filter/predicate/permanent/UnblockedPredicate.java +++ b/Mage/src/mage/filter/predicate/permanent/UnblockedPredicate.java @@ -48,7 +48,8 @@ public class UnblockedPredicate implements Predicate { if ((game.getPhase().getStep().getType() == PhaseStep.DECLARE_BLOCKERS && game.getStep().getStepPart() == Step.StepPart.PRIORITY) || game.getPhase().getStep().getType() == PhaseStep.FIRST_COMBAT_DAMAGE - || game.getPhase().getStep().getType() == PhaseStep.COMBAT_DAMAGE) { + || game.getPhase().getStep().getType() == PhaseStep.COMBAT_DAMAGE + || game.getPhase().getStep().getType() == PhaseStep.END_COMBAT) { CombatGroup combatGroup = game.getCombat().findGroup(input.getId()); if (combatGroup != null) { return combatGroup.getBlockers().isEmpty(); diff --git a/Mage/src/mage/game/CardAttribute.java b/Mage/src/mage/game/CardAttribute.java new file mode 100644 index 00000000000..95b4659ab7d --- /dev/null +++ b/Mage/src/mage/game/CardAttribute.java @@ -0,0 +1,29 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.game; + +import java.io.Serializable; +import mage.ObjectColor; +import mage.cards.Card; + +/** + * This class saves changed attributes of cards (e.g. in graveyard, exile or player hands or libraries). + * + * @author LevelX2 + */ +public class CardAttribute implements Serializable { + + protected ObjectColor color; + + public CardAttribute(Card card) { + color = card.getColor(null).copy(); + } + + public ObjectColor getColor() { + return color; + } + +} diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 817c65571c4..3743aae59c4 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -208,9 +208,7 @@ public interface Game extends MageItem, Serializable { */ PreventionEffectData preventDamage(GameEvent event, Ability source, Game game, boolean preventAllDamage); - //game play methods void start(UUID choosingPlayerId); - void start(UUID choosingPlayerId, GameOptions options); void resume(); void pause(); boolean isPaused(); @@ -226,7 +224,8 @@ public interface Game extends MageItem, Serializable { void timerTimeout(UUID playerId); void idleTimeout(UUID playerId); void concede(UUID playerId); - void setManaPoolMode(UUID playerId, boolean autoPayment); + void setManaPaymentMode(UUID playerId, boolean autoPayment); + void setManaPaymentModeRestricted(UUID playerId, boolean autoPaymentRestricted); void undo(UUID playerId); void emptyManaPools(); void addEffect(ContinuousEffect continuousEffect, Ability source); @@ -293,5 +292,10 @@ public interface Game extends MageItem, Serializable { int getPriorityTime(); void setPriorityTime(int priorityTime); UUID getStartingPlayerId(); + + void saveRollBackGameState(); + boolean canRollbackTurns(int turnsToRollback); + void rollbackTurns(int turnsToRollback); + boolean executingRollback(); +} -} diff --git a/Mage/src/mage/game/GameCommanderImpl.java b/Mage/src/mage/game/GameCommanderImpl.java index b5e4fae424f..4a12851b2e2 100644 --- a/Mage/src/mage/game/GameCommanderImpl.java +++ b/Mage/src/mage/game/GameCommanderImpl.java @@ -75,7 +75,7 @@ public abstract class GameCommanderImpl extends GameImpl { } @Override - protected void init(UUID choosingPlayerId, GameOptions gameOptions) { + protected void init(UUID choosingPlayerId) { Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects")); //Move commander to command zone for (UUID playerId: state.getPlayerList(startingPlayerId)) { @@ -83,9 +83,10 @@ public abstract class GameCommanderImpl extends GameImpl { if (player != null){ if (player.getSideboard().size() > 0){ Card commander = getCard((UUID)player.getSideboard().toArray()[0]); - if (commander != null) { + if (commander != null) { player.setCommanderId(commander.getId()); commander.moveToZone(Zone.COMMAND, null, this, true); + commander.getAbilities().setControllerId(player.getId()); ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary)); ability.addEffect(new CommanderCostModification(commander.getId())); ability.addEffect(new CommanderManaReplacementEffect(player.getId(), CardUtil.getColorIdentity(commander))); @@ -100,7 +101,7 @@ public abstract class GameCommanderImpl extends GameImpl { } this.getState().addAbility(ability, null); - super.init(choosingPlayerId, gameOptions); + super.init(choosingPlayerId); if (startingPlayerSkipsDraw) { state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); } diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 8251d6e7adc..23561b34258 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -116,6 +116,7 @@ import mage.players.Players; import mage.target.Target; import mage.target.TargetPermanent; import mage.target.TargetPlayer; +import mage.util.GameLog; import mage.util.functions.ApplyToPermanent; import mage.watchers.Watchers; import mage.watchers.common.BlockedAttackerWatcher; @@ -129,6 +130,8 @@ import org.apache.log4j.Logger; public abstract class GameImpl implements Game, Serializable { + private static final int ROLLBACK_TURNS_MAX = 4; + private static final transient Logger logger = Logger.getLogger(GameImpl.class); private static final FilterPermanent filterAura = new FilterPermanent(); @@ -155,7 +158,8 @@ public abstract class GameImpl implements Game, Serializable { private transient Object customData; protected boolean simulation = false; - protected final UUID id; + protected final UUID id; + protected boolean ready; protected transient TableEventSource tableEventSource = new TableEventSource(); protected transient PlayerQueryEventSource playerQueryEventSource = new PlayerQueryEventSource(); @@ -170,6 +174,9 @@ public abstract class GameImpl implements Game, Serializable { protected GameState state; private transient Stack savedStates = new Stack<>(); protected transient GameStates gameStates = new GameStates(); + // game states to allow player roll back + protected transient Map gameStatesRollBack = new HashMap<>(); + protected boolean executingRollback; protected Date startTime; protected Date endTime; @@ -206,7 +213,7 @@ public abstract class GameImpl implements Game, Serializable { this.attackOption = attackOption; this.state = new GameState(); this.startLife = startLife; - // this.actions = new LinkedList(); + this.executingRollback = false; } public GameImpl(final GameImpl game) { @@ -232,7 +239,6 @@ public abstract class GameImpl implements Game, Serializable { copyCount++; copyTime += (System.currentTimeMillis() - t1); } -// this.actions = new LinkedList(); this.stateCheckRequired = game.stateCheckRequired; this.scorePlayer = game.scorePlayer; this.scopeRelevant = game.scopeRelevant; @@ -268,7 +274,10 @@ public abstract class GameImpl implements Game, Serializable { @Override public GameOptions getOptions() { - return gameOptions; + if (gameOptions != null) { + return gameOptions; + } + return new GameOptions(); // happens during the first game updates } @Override @@ -610,23 +619,17 @@ public abstract class GameImpl implements Game, Serializable { return 0; } - @Override - public void start(UUID choosingPlayerId) { - start(choosingPlayerId, this.gameOptions != null ? gameOptions : GameOptions.getDefault()); - } - @Override public void cleanUp() { gameCards.clear(); } @Override - public void start(UUID choosingPlayerId, GameOptions options) { + public void start(UUID choosingPlayerId) { startTime = new Date(); - this.gameOptions = options; if (state.getPlayers().values().iterator().hasNext()) { scorePlayer = state.getPlayers().values().iterator().next(); - init(choosingPlayerId, options); + init(choosingPlayerId); play(startingPlayerId); } } @@ -731,13 +734,26 @@ public abstract class GameImpl implements Game, Serializable { } private boolean playTurn(Player player) { - this.logStartOfTurn(player); - if (checkStopOnTurnOption()) { - return false; - } - state.setActivePlayerId(player.getId()); - player.becomesActivePlayer(); - state.getTurn().play(this, player.getId()); + do { + if (executingRollback) { + executingRollback = false; + player = getPlayer(state.getActivePlayerId()); + for (Player playerObject: getPlayers().values()) { + if (playerObject.isInGame()) { + playerObject.abortReset(); + } + } + } else { + state.setActivePlayerId(player.getId()); + saveRollBackGameState(); + } + this.logStartOfTurn(player); + if (checkStopOnTurnOption()) { + return false; + } + state.getTurn().play(this, player); + } while (executingRollback); + if (isPaused() || gameOver(null)) { return false; } @@ -778,7 +794,7 @@ public abstract class GameImpl implements Game, Serializable { return false; } - protected void init(UUID choosingPlayerId, GameOptions gameOptions) { + protected void init(UUID choosingPlayerId) { for (Player player: state.getPlayers().values()) { player.beginTurn(this); // init only if match is with timer (>0) and time left was not set yet (== MAX_VALUE). @@ -1108,12 +1124,20 @@ public abstract class GameImpl implements Game, Serializable { @Override - public synchronized void setManaPoolMode(UUID playerId, boolean autoPayment) { + public synchronized void setManaPaymentMode(UUID playerId, boolean autoPayment) { Player player = state.getPlayer(playerId); if (player != null) { player.getManaPool().setAutoPayment(autoPayment); } } + + @Override + public synchronized void setManaPaymentModeRestricted(UUID playerId, boolean autoPaymentRestricted) { + Player player = state.getPlayer(playerId); + if (player != null) { + player.getManaPool().setAutoPaymentRestricted(autoPaymentRestricted); + } + } @Override public void playPriority(UUID activePlayerId, boolean resuming) { @@ -1151,6 +1175,9 @@ public abstract class GameImpl implements Game, Serializable { } // resetPassed should be called if player performs any action if (player.priority(this)) { + if(executingRollback()) { + return; + } applyEffects(); } if (isPaused()) { @@ -2058,6 +2085,11 @@ public abstract class GameImpl implements Game, Serializable { Permanent attachedTo = getPermanent(perm.getAttachedTo()); if (attachedTo != null) { attachedTo.removeAttachment(perm.getId(), this); + } else { + Player attachedToPlayer = getPlayer(perm.getAttachedTo()); + if (attachedToPlayer != null) { + attachedToPlayer.removeAttachment(perm, this); + } } } // check if it's a creature and must be removed from combat @@ -2100,12 +2132,9 @@ public abstract class GameImpl implements Game, Serializable { } } - // Update players in range of - for (Player leftPlayer :this.getPlayers().values()) { - if (leftPlayer.isInGame()) { - leftPlayer.otherPlayerLeftGame(this); - } - } + // 801.2c The particular players within each player‘s range of influence are determined as each turn begins. + // So no update of range if influence yet + } @Override @@ -2564,6 +2593,51 @@ public abstract class GameImpl implements Game, Serializable { } } + @Override + public void saveRollBackGameState() { + if (gameOptions.rollbackTurnsAllowed) { + int toDelete = getTurnNum()- ROLLBACK_TURNS_MAX; + if (toDelete > 0 && gameStatesRollBack.containsKey(toDelete)) { + gameStatesRollBack.remove(toDelete); + } + gameStatesRollBack.put(getTurnNum(), state.copy()); + } + } + @Override + public boolean canRollbackTurns(int turnsToRollback) { + int turnToGoTo = getTurnNum() - turnsToRollback; + return turnToGoTo > 0 && gameStatesRollBack.containsKey(turnToGoTo); + } + + @Override + public synchronized void rollbackTurns(int turnsToRollback) { + if (gameOptions.rollbackTurnsAllowed) { + int turnToGoTo = getTurnNum() - turnsToRollback; + if (turnToGoTo < 1 || !gameStatesRollBack.containsKey(turnToGoTo)) { + informPlayers(GameLog.getPlayerRequestColoredText("Player request: It's not possible to rollback " + turnsToRollback +" turn(s)")); + } else { + GameState restore = gameStatesRollBack.get(turnToGoTo); + if (restore != null) { + informPlayers(GameLog.getPlayerRequestColoredText("Player request: Rolling back to start of turn " + restore.getTurnNum())); + for (Player playerObject: getPlayers().values()) { + if (playerObject.isHuman() && playerObject.isInGame()) { + playerObject.abort(); + } + } + state.restoreForRollBack(restore); + // because restore uses the objects without copy each copy the state again + gameStatesRollBack.put(getTurnNum(), state.copy()); + executingRollback = true; + fireUpdatePlayersEvent(); + } + } + } + } + + @Override + public boolean executingRollback() { + return executingRollback; + } } diff --git a/Mage/src/mage/game/GameOptions.java b/Mage/src/mage/game/GameOptions.java index 63ba1888368..7767880a589 100644 --- a/Mage/src/mage/game/GameOptions.java +++ b/Mage/src/mage/game/GameOptions.java @@ -37,4 +37,9 @@ public class GameOptions implements Serializable { * If true, library won't be shuffled at the beginning of the game */ public boolean skipInitShuffling = false; + + /** + * If true, players can roll back turn if all players agree + */ + public boolean rollbackTurnsAllowed = true; } diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index 332ead3e168..ebfdc555a19 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -77,19 +77,19 @@ public class GameState implements Serializable, Copyable { private final Players players; private final PlayerList playerList; - private final Turn turn; + private UUID choosingPlayerId; // player that makes a choice at game start + // revealed cards >, will be reset if all players pass priority private final Revealed revealed; private final Map lookedAt = new HashMap<>(); - private final DelayedTriggeredAbilities delayed; - private final SpecialActions specialActions; - private final TurnMods turnMods; - private final Watchers watchers; - + private DelayedTriggeredAbilities delayed; + private SpecialActions specialActions; + private Watchers watchers; + private Turn turn; + private TurnMods turnMods; private UUID activePlayerId; // playerId which turn it is private UUID priorityPlayerId; // player that has currently priority - private UUID choosingPlayerId; // player that makes a choice at game start private SpellStack stack; private Command command; private Exile exile; @@ -109,6 +109,7 @@ public class GameState implements Serializable, Copyable { private Map zones = new HashMap<>(); private List simultaneousEvents = new ArrayList<>(); private Map cardState = new HashMap<>(); + private Map cardAttribute = new HashMap<>(); private Map zoneChangeCounter = new HashMap<>(); private Map copiedCards = new HashMap<>(); private int permanentOrderNumber; @@ -134,21 +135,24 @@ public class GameState implements Serializable, Copyable { public GameState(final GameState state) { this.players = state.players.copy(); this.playerList = state.playerList.copy(); + this.choosingPlayerId = state.choosingPlayerId; + this.revealed = state.revealed.copy(); + this.lookedAt.putAll(state.lookedAt); + this.gameOver = state.gameOver; + this.paused = state.paused; + this.activePlayerId = state.activePlayerId; this.priorityPlayerId = state.priorityPlayerId; - this.choosingPlayerId = state.choosingPlayerId; this.turn = state.turn.copy(); + this.stack = state.stack.copy(); this.command = state.command.copy(); this.exile = state.exile.copy(); - this.revealed = state.revealed.copy(); - this.lookedAt.putAll(state.lookedAt); this.battlefield = state.battlefield.copy(); this.turnNum = state.turnNum; this.stepNum = state.stepNum; this.extraTurn = state.extraTurn; this.legendaryRuleActive = state.legendaryRuleActive; - this.gameOver = state.gameOver; this.effects = state.effects.copy(); for (TriggeredAbility trigger: state.triggered) { this.triggered.add(trigger.copy()); @@ -163,7 +167,6 @@ public class GameState implements Serializable, Copyable { this.values.put(entry.getKey(), entry.getValue()); } this.zones.putAll(state.zones); - this.paused = state.paused; this.simultaneousEvents.addAll(state.simultaneousEvents); for (Map.Entry entry: state.cardState.entrySet()) { cardState.put(entry.getKey(), entry.getValue().copy()); @@ -172,6 +175,43 @@ public class GameState implements Serializable, Copyable { this.copiedCards.putAll(state.copiedCards); this.permanentOrderNumber = state.permanentOrderNumber; } + + public void restoreForRollBack(GameState state) { + restore(state); + this.turn = state.turn; + } + + public void restore(GameState state) { + this.activePlayerId = state.activePlayerId; + this.priorityPlayerId = state.priorityPlayerId; + this.stack = state.stack; + this.command = state.command; + this.exile = state.exile; + this.battlefield = state.battlefield; + this.turnNum = state.turnNum; + this.stepNum = state.stepNum; + this.extraTurn = state.extraTurn; + this.legendaryRuleActive = state.legendaryRuleActive; + this.effects = state.effects; + this.triggered = state.triggered; + this.triggers = state.triggers; + this.delayed = state.delayed; + this.specialActions = state.specialActions; + this.combat = state.combat; + this.turnMods = state.turnMods; + this.watchers = state.watchers; + this.values = state.values; + for (Player copyPlayer: state.players.values()) { + Player origPlayer = players.get(copyPlayer.getId()); + origPlayer.restore(copyPlayer); + } + this.zones = state.zones; + this.simultaneousEvents = state.simultaneousEvents; + this.cardState = state.cardState; + this.zoneChangeCounter = state.zoneChangeCounter; + this.copiedCards = state.copiedCards; + this.permanentOrderNumber = state.permanentOrderNumber; + } @Override public GameState copy() { @@ -558,28 +598,6 @@ public class GameState implements Serializable, Copyable { zones.put(id, zone); } - public void restore(GameState state) { - this.stack = state.stack; - this.command = state.command; - this.effects = state.effects; - this.triggers = state.triggers; - this.triggered = state.triggered; - this.combat = state.combat; - this.exile = state.exile; - this.battlefield = state.battlefield; - this.zones = state.zones; - this.values = state.values; - for (Player copyPlayer: state.players.values()) { - Player origPlayer = players.get(copyPlayer.getId()); - origPlayer.restore(copyPlayer); - } - this.simultaneousEvents = state.simultaneousEvents; - this.cardState = state.cardState; - this.zoneChangeCounter = state.zoneChangeCounter; - this.copiedCards = state.copiedCards; - this.permanentOrderNumber = state.permanentOrderNumber; - } - public void addSimultaneousEvent(GameEvent event, Game game) { simultaneousEvents.add(event); } @@ -814,6 +832,7 @@ public class GameState implements Serializable, Copyable { for (CardState state: cardState.values()) { state.clearAbilities(); } + cardAttribute.clear(); } public void clear() { @@ -840,6 +859,7 @@ public class GameState implements Serializable, Copyable { values.clear(); zones.clear(); simultaneousEvents.clear(); + copiedCards.clear(); permanentOrderNumber = 0; } @@ -874,11 +894,23 @@ public class GameState implements Serializable, Copyable { public CardState getCardState(UUID cardId) { if (!cardState.containsKey(cardId)) { cardState.put(cardId, new CardState()); - // cardState.putIfAbsent(cardId, new CardState()); } return cardState.get(cardId); } + public CardAttribute getCardAttribute(UUID cardId) { + return cardAttribute.get(cardId); + } + + public CardAttribute getCreateCardAttribute(Card card) { + CardAttribute cardAtt = cardAttribute.get(card.getId()); + if (cardAtt == null) { + cardAtt = new CardAttribute(card); + cardAttribute.put(card.getId(), cardAtt); + } + return cardAtt; + } + public void addWatcher(Watcher watcher) { this.watchers.add(watcher); } diff --git a/Mage/src/mage/game/GameStates.java b/Mage/src/mage/game/GameStates.java index ab624bb9978..e88e45c27d7 100644 --- a/Mage/src/mage/game/GameStates.java +++ b/Mage/src/mage/game/GameStates.java @@ -42,8 +42,12 @@ public class GameStates implements Serializable { private static final transient Logger logger = Logger.getLogger(GameStates.class); -// private List states = new LinkedList(); - private final List states = new LinkedList<>(); +// private final List states; + private final List states; + + public GameStates() { + this.states = new LinkedList<>(); + } public void save(GameState gameState) { // states.add(new Copier().copyCompressed(gameState)); @@ -60,8 +64,8 @@ public class GameStates implements Serializable { while (states.size() > index + 1) { states.remove(states.size() - 1); } -// return new Copier().uncompressCopy(states.get(index)); logger.trace("Rolling back state: " + index); +// return new Copier().uncompressCopy(states.get(index)); return states.get(index); } return null; @@ -78,7 +82,7 @@ public class GameStates implements Serializable { public GameState get(int index) { if (index < states.size()) { - // return new Copier().uncompressCopy(states.get(index)); +// return new Copier().uncompressCopy(states.get(index)); return states.get(index); } return null; diff --git a/Mage/src/mage/game/GameTinyLeadersImpl.java b/Mage/src/mage/game/GameTinyLeadersImpl.java index d25f23cea01..6addf9f0c9e 100644 --- a/Mage/src/mage/game/GameTinyLeadersImpl.java +++ b/Mage/src/mage/game/GameTinyLeadersImpl.java @@ -74,7 +74,7 @@ public abstract class GameTinyLeadersImpl extends GameImpl{ } @Override - protected void init(UUID choosingPlayerId, GameOptions gameOptions) { + protected void init(UUID choosingPlayerId) { Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects")); //Move tiny leader to command zone for (UUID playerId: state.getPlayerList(startingPlayerId)) { @@ -101,7 +101,7 @@ public abstract class GameTinyLeadersImpl extends GameImpl{ } this.getState().addAbility(ability, null); - super.init(choosingPlayerId, gameOptions); + super.init(choosingPlayerId); if (startingPlayerSkipsDraw) { state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); } diff --git a/Mage/src/mage/game/command/Commander.java b/Mage/src/mage/game/command/Commander.java index 0602bafbec1..ff0a5565263 100644 --- a/Mage/src/mage/game/command/Commander.java +++ b/Mage/src/mage/game/command/Commander.java @@ -149,8 +149,8 @@ public class Commander implements CommandObject{ } @Override - public ObjectColor getColor() { - return card.getColor(); + public ObjectColor getColor(Game game) { + return card.getColor(game); } @Override diff --git a/Mage/src/mage/game/command/Emblem.java b/Mage/src/mage/game/command/Emblem.java index 468aa9cc730..79d74ca8885 100644 --- a/Mage/src/mage/game/command/Emblem.java +++ b/Mage/src/mage/game/command/Emblem.java @@ -150,7 +150,7 @@ public class Emblem implements CommandObject { } @Override - public ObjectColor getColor() { + public ObjectColor getColor(Game game) { return emptyColor; } diff --git a/Mage/src/mage/game/match/MatchOptions.java b/Mage/src/mage/game/match/MatchOptions.java index 71ff1cd2501..7fce8625475 100644 --- a/Mage/src/mage/game/match/MatchOptions.java +++ b/Mage/src/mage/game/match/MatchOptions.java @@ -53,6 +53,7 @@ public class MatchOptions implements Serializable { protected List playerTypes = new ArrayList<>(); protected String password; protected SkillLevel skillLevel; + protected boolean rollbackTurnsAllowed; /** * Time each player has during the game to play using his\her priority. @@ -159,4 +160,12 @@ public class MatchOptions implements Serializable { public void setSkillLevel(SkillLevel skillLevel) { this.skillLevel = skillLevel; } + + public boolean isRollbackTurnsAllowed() { + return rollbackTurnsAllowed; + } + + public void setRollbackTurnsAllowed(boolean rollbackTurnsAllowed) { + this.rollbackTurnsAllowed = rollbackTurnsAllowed; + } } diff --git a/Mage/src/mage/game/match/MatchPlayer.java b/Mage/src/mage/game/match/MatchPlayer.java index af956dcc747..6f77ebfe8a5 100644 --- a/Mage/src/mage/game/match/MatchPlayer.java +++ b/Mage/src/mage/game/match/MatchPlayer.java @@ -28,6 +28,7 @@ package mage.game.match; +import java.io.Serializable; import mage.cards.Card; import mage.cards.decks.Deck; import mage.players.Player; @@ -36,7 +37,10 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class MatchPlayer { +public class MatchPlayer implements Serializable { + + private static final long serialVersionUID = 42L; + private int wins; private boolean matchWinner; @@ -45,7 +49,7 @@ public class MatchPlayer { private final String name; private boolean quit; - private final boolean timerTimeout; + //private final boolean timerTimeout; private boolean doneSideboarding; private int priorityTimeLeft; @@ -56,7 +60,7 @@ public class MatchPlayer { this.wins = 0; this.doneSideboarding = true; this.quit = false; - this.timerTimeout = false; + //this.timerTimeout = false; this.name = player.getName(); this.matchWinner = false; } diff --git a/Mage/src/mage/game/permanent/PermanentCard.java b/Mage/src/mage/game/permanent/PermanentCard.java index 220905b133e..c6994cb72ed 100644 --- a/Mage/src/mage/game/permanent/PermanentCard.java +++ b/Mage/src/mage/game/permanent/PermanentCard.java @@ -99,7 +99,7 @@ public class PermanentCard extends PermanentImpl { this.abilities.setSourceId(objectId); this.cardType.clear(); this.cardType.addAll(card.getCardType()); - this.color = card.getColor().copy(); + this.color = card.getColor(null).copy(); this.manaCost = card.getManaCost().copy(); this.power = card.getPower().copy(); this.toughness = card.getToughness().copy(); diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index 098313d6337..e50d04cb09f 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -30,7 +30,6 @@ package mage.game.permanent; import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -39,6 +38,7 @@ import java.util.Map; import java.util.UUID; import mage.MageObject; import mage.MageObjectReference; +import mage.ObjectColor; import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffect; @@ -1360,5 +1360,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { public void setCreateOrder(int createOrder) { this.createOrder = createOrder; } - + + @Override + public ObjectColor getColor(Game game) { + return color; + } + + } diff --git a/Mage/src/mage/game/permanent/PermanentToken.java b/Mage/src/mage/game/permanent/PermanentToken.java index 69d2aa37442..be5e63776a0 100644 --- a/Mage/src/mage/game/permanent/PermanentToken.java +++ b/Mage/src/mage/game/permanent/PermanentToken.java @@ -82,7 +82,7 @@ public class PermanentToken extends PermanentImpl { this.getManaCost().add(cost.copy()); } this.cardType = token.getCardType(); - this.color = token.getColor().copy(); + this.color = token.getColor(game).copy(); this.power.initValue(token.getPower().getValue()); this.toughness.initValue(token.getToughness().getValue()); this.supertype = token.getSupertype(); diff --git a/Mage/src/mage/game/permanent/token/GolemToken.java b/Mage/src/mage/game/permanent/token/GolemToken.java index 0c967c6dd0d..273943d7fa1 100644 --- a/Mage/src/mage/game/permanent/token/GolemToken.java +++ b/Mage/src/mage/game/permanent/token/GolemToken.java @@ -37,7 +37,12 @@ import mage.MageInt; public class GolemToken extends Token { public GolemToken() { + this("SOM"); + } + + public GolemToken(String setCode) { super("Golem", "a 3/3 colorless Golem artifact creature token"); + setOriginalExpansionSetCode(setCode); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); subtype.add("Golem"); diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index 4676f22cf88..bbb1ef01d8f 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -37,39 +37,33 @@ import mage.Mana; import mage.ObjectColor; import mage.abilities.Abilities; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.SpellAbility; import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.Cost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.keyword.BestowAbility; import mage.abilities.keyword.MorphAbility; import mage.cards.Card; import mage.cards.SplitCard; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.SpellAbilityType; import mage.constants.Zone; import mage.counters.Counter; import mage.counters.Counters; -import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; import mage.players.Player; -import mage.target.Target; -import mage.target.TargetAmount; import mage.util.GameLog; /** * * @author BetaSteward_at_googlemail.com */ -public class Spell implements StackObject, Card { +public class Spell extends StackObjImpl implements Card { private final List spellCards = new ArrayList<>(); private final List spellAbilities = new ArrayList<>(); @@ -87,7 +81,7 @@ public class Spell implements StackObject, Card { public Spell(Card card, SpellAbility ability, UUID controllerId, Zone fromZone) { this.card = card; - this.color = card.getColor().copy(); + this.color = card.getColor(null).copy(); id = ability.getId(); this.ability = ability; this.ability.setControllerId(controllerId); @@ -313,213 +307,8 @@ public class Spell implements StackObject, Card { } } } - - /** - * Choose new targets for the spell - * - * @param game - * @param playerId Player UUID who changes the targets. - * @return - */ - public boolean chooseNewTargets(Game game, UUID playerId) { - return chooseNewTargets(game, playerId, false, false, null); - } - - /** - * 114.6. Some effects allow a player to change the target(s) of a spell or - * ability, and other effects allow a player to choose new targets for a - * spell or ability. - * - * 114.6a If an effect allows a player to "change the - * target(s)" of a spell or ability, each target can be changed only to - * another legal target. If a target can't be changed to another legal - * target, the original target is unchanged, even if the original target is - * itself illegal by then. If all the targets aren't changed to other legal - * targets, none of them are changed. - * - * 114.6b If an effect allows a player to "change a target" of a - * spell or ability, the process described in rule 114.6a - * is followed, except that only one of those targets may be changed - * (rather than all of them or none of them). - * - * 114.6c If an effect allows a - * player to "change any targets" of a spell or ability, the process - * described in rule 114.6a is followed, except that any number of those - * targets may be changed (rather than all of them or none of them). - * - * 114.6d If an effect allows a player to "choose new targets" for a spell or - * ability, the player may leave any number of the targets unchanged, even - * if those targets would be illegal. If the player chooses to change some - * or all of the targets, the new targets must be legal and must not cause - * any unchanged targets to become illegal. - * - * 114.6e When changing targets or - * choosing new targets for a spell or ability, only the final set of - * targets is evaluated to determine whether the change is legal. - * - * Example: Arc Trail is a sorcery that reads "Arc Trail deals 2 damage to - * target creature or player and 1 damage to another target creature or - * player." The current targets of Arc Trail are Runeclaw Bear and Llanowar - * Elves, in that order. You cast Redirect, an instant that reads "You may - * choose new targets for target spell," targeting Arc Trail. You can change - * the first target to Llanowar Elves and change the second target to - * Runeclaw Bear. - * - * 114.7. Modal spells and abilities may have different targeting - * requirements for each mode. An effect that allows a player to change the - * target(s) of a modal spell or ability, or to choose new targets for a - * modal spell or ability, doesn't allow that player to change its mode. - * (See rule 700.2.) - * - * 706.10c Some effects copy a spell or ability and state that its - * controller may choose new targets for the copy. The player may leave any - * number of the targets unchanged, even if those targets would be illegal. - * If the player chooses to change some or all of the targets, the new - * targets must be legal. Once the player has decided what the copy's - * targets will be, the copy is put onto the stack with those targets. - * - * @param game - * @param playerId - player that can/has to change the taregt of the spell - * @param forceChange - does only work for targets with maximum of one targetId - * @param onlyOneTarget - 114.6b one target must be changed to another target - * @param filterNewTarget restriction for the new target, if null nothing is cheched - * @return - */ - @Override - public boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { - Player player = game.getPlayer(playerId); - if (player != null) { - StringBuilder newTargetDescription = new StringBuilder(); - // Fused split spells or spells where "Splice on Arcane" was used can have more than one ability - for (SpellAbility spellAbility : spellAbilities) { - // Some spells can have more than one mode - for (UUID modeId : spellAbility.getModes().getSelectedModes()) { - Mode mode = spellAbility.getModes().get(modeId); - for (Target target : mode.getTargets()) { - Target newTarget = chooseNewTarget(player, spellAbility, mode, target, forceChange, filterNewTarget, game); - // clear the old target and copy all targets from new target - target.clearChosen(); - for (UUID targetId : newTarget.getTargets()) { - target.addTarget(targetId, newTarget.getTargetAmount(targetId), spellAbility, game, false); - } - - } - newTargetDescription.append(getSpellAbility().getTargetDescription(mode.getTargets(), game)); - } - - } - if (newTargetDescription.length() > 0 && !game.isSimulation()) { - game.informPlayers(this.getName() + " is now " + newTargetDescription.toString()); - } - return true; - } - return false; - } - - /** - * Handles the change of one target instance of a mode - * - * @param player - player that can choose the new target - * @param spellAbility - * @param mode - * @param target - * @param forceChange - * @param game - * @return - */ - private Target chooseNewTarget(Player player, SpellAbility spellAbility, Mode mode, Target target, boolean forceChange, FilterPermanent filterNewTarget, Game game) { - Target newTarget = target.copy(); - newTarget.clearChosen(); - for (UUID targetId : target.getTargets()) { - String targetNames = getNamesOftargets(targetId, game); - // change the target? - if (targetNames != null - && (forceChange || player.chooseUse(mode.getEffects().get(0).getOutcome(), "Change this target: " + targetNames + "?", game))) { - // choose exactly one other target - if (forceChange && target.possibleTargets(this.getSourceId(), getControllerId(), game).size() > 1) { // controller of spell must be used (e.g. TargetOpponent) - int iteration = 0; - do { - if (iteration > 0 && !game.isSimulation()) { - game.informPlayer(player, "You may only select exactly one target that must be different from the origin target!"); - } - iteration++; - newTarget.clearChosen(); - // TODO: Distinction between "spell controller" and "player that can change the target" - here player is used for both - newTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), spellAbility, game); - // check target restriction - if (newTarget.getFirstTarget() != null && filterNewTarget != null) { - Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); - if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { - game.informPlayer(player, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")"); - newTarget.clearChosen(); - } - } - } while (player.isInGame() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1)); - // choose a new target - } else { - // build a target definition with exactly one possible target to select that replaces old target - Target tempTarget = target.copy(); - if (target instanceof TargetAmount) { - ((TargetAmount)tempTarget).setAmountDefinition(new StaticValue(target.getTargetAmount(targetId))); - } - tempTarget.setMinNumberOfTargets(1); - tempTarget.setMaxNumberOfTargets(1); - boolean again; - do { - again = false; - tempTarget.clearChosen(); - if (!tempTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), spellAbility, game)) { - if (player.chooseUse(Outcome.Benefit, "No target object selected. Reset to original target?", game)) { - // use previous target no target was selected - newTarget.addTarget(targetId, target.getTargetAmount(targetId), spellAbility, game, false); - } else { - again = true; - } - } else { - // if possible add the alternate Target - it may not be included in the old definition nor in the already selected targets of the new definition - if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) { - if(player.isHuman()) { - game.informPlayer(player, "This target was already selected from origin spell. You can only keep this target!"); - again = true; - } else { - newTarget.addTarget(targetId, target.getTargetAmount(targetId), spellAbility, game, false); - } - } else if (newTarget.getFirstTarget() != null && filterNewTarget != null) { - Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); - if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { - game.informPlayer(player, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")"); - again = true; - } - } else { - // valid target was selected, add it to the new target definition - newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), spellAbility, game, false); - } - } - } while (again && player.isInGame()); - } - } - // keep the target - else { - newTarget.addTarget(targetId, target.getTargetAmount(targetId), spellAbility, game, false); - } - } - return newTarget; - } - - private String getNamesOftargets(UUID targetId, Game game) { - MageObject object = game.getObject(targetId); - String name = null; - if (object == null) { - Player targetPlayer = game.getPlayer(targetId); - if (targetPlayer != null) { - name = targetPlayer.getLogName(); - } - } else { - name = object.getName(); - } - return name; - } + @Override public void counter(UUID sourceId, Game game) { @@ -554,7 +343,13 @@ public class Spell implements StackObject, Card { @Override public String getIdName() { - return getName() + " ["+getId().toString().substring(0,3) +"]"; + String idName; + if (card != null) { + idName = card.getId().toString().substring(0,3); + } else { + idName = getId().toString().substring(0,3); + } + return getName() + " ["+idName+"]"; } @Override @@ -636,7 +431,7 @@ public class Spell implements StackObject, Card { } @Override - public ObjectColor getColor() { + public ObjectColor getColor(Game game) { return color; } diff --git a/Mage/src/mage/game/stack/StackAbility.java b/Mage/src/mage/game/stack/StackAbility.java index 29804bbf9bc..021ac659dbe 100644 --- a/Mage/src/mage/game/stack/StackAbility.java +++ b/Mage/src/mage/game/stack/StackAbility.java @@ -54,15 +54,10 @@ import mage.target.Targets; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.cards.Card; import mage.constants.AbilityWord; -import mage.constants.Outcome; -import mage.filter.FilterPermanent; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.TargetAmount; import mage.util.GameLog; import mage.watchers.Watcher; @@ -70,7 +65,7 @@ import mage.watchers.Watcher; * * @author BetaSteward_at_googlemail.com */ -public class StackAbility implements StackObject, Ability { +public class StackAbility extends StackObjImpl implements Ability { private static List emptyCardType = new ArrayList<>(); private static List emptyString = new ArrayList<>(); @@ -180,7 +175,7 @@ public class StackAbility implements StackObject, Ability { } @Override - public ObjectColor getColor() { + public ObjectColor getColor(Game game) { return emptyColor; } @@ -551,197 +546,9 @@ public class StackAbility implements StackObject, Ability { throw new UnsupportedOperationException("Not supported."); } - /** - * 114.6. Some effects allow a player to change the target(s) of a spell or - * ability, and other effects allow a player to choose new targets for a - * spell or ability. - * - * 114.6a If an effect allows a player to "change the - * target(s)" of a spell or ability, each target can be changed only to - * another legal target. If a target can't be changed to another legal - * target, the original target is unchanged, even if the original target is - * itself illegal by then. If all the targets aren't changed to other legal - * targets, none of them are changed. - * - * 114.6b If an effect allows a player to "change a target" of a - * spell or ability, the process described in rule 114.6a - * is followed, except that only one of those targets may be changed - * (rather than all of them or none of them). - * - * 114.6c If an effect allows a - * player to "change any targets" of a spell or ability, the process - * described in rule 114.6a is followed, except that any number of those - * targets may be changed (rather than all of them or none of them). - * - * 114.6d If an effect allows a player to "choose new targets" for a spell or - * ability, the player may leave any number of the targets unchanged, even - * if those targets would be illegal. If the player chooses to change some - * or all of the targets, the new targets must be legal and must not cause - * any unchanged targets to become illegal. - * - * 114.6e When changing targets or - * choosing new targets for a spell or ability, only the final set of - * targets is evaluated to determine whether the change is legal. - * - * Example: Arc Trail is a sorcery that reads "Arc Trail deals 2 damage to - * target creature or player and 1 damage to another target creature or - * player." The current targets of Arc Trail are Runeclaw Bear and Llanowar - * Elves, in that order. You cast Redirect, an instant that reads "You may - * choose new targets for target spell," targeting Arc Trail. You can change - * the first target to Llanowar Elves and change the second target to - * Runeclaw Bear. - * - * 114.7. Modal spells and abilities may have different targeting - * requirements for each mode. An effect that allows a player to change the - * target(s) of a modal spell or ability, or to choose new targets for a - * modal spell or ability, doesn't allow that player to change its mode. - * (See rule 700.2.) - * - * 706.10c Some effects copy a spell or ability and state that its - * controller may choose new targets for the copy. The player may leave any - * number of the targets unchanged, even if those targets would be illegal. - * If the player chooses to change some or all of the targets, the new - * targets must be legal. Once the player has decided what the copy's - * targets will be, the copy is put onto the stack with those targets. - * - * @param game - * @param playerId - player that can/has to change the target of the ability - * @param forceChange - does only work for targets with maximum of one targetId - * @param onlyOneTarget - 114.6b one target must be changed to another target - * @param filterNewTarget restriction for the new target, if null nothing is cheched - * @return - */ @Override - public boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { - Player player = game.getPlayer(playerId); - if (player != null) { - StringBuilder newTargetDescription = new StringBuilder(); - // Some abilities can have more than one mode - for (UUID modeId : ability.getModes().getSelectedModes()) { - Mode mode = ability.getModes().get(modeId); - for (Target target : mode.getTargets()) { - Target newTarget = chooseNewTarget(player, getStackAbility(), mode, target, forceChange, filterNewTarget, game); - // clear the old target and copy all targets from new target - target.clearChosen(); - for (UUID targetId : newTarget.getTargets()) { - target.addTarget(targetId, newTarget.getTargetAmount(targetId), ability, game, false); - } - - } - newTargetDescription.append(((AbilityImpl)ability).getTargetDescription(mode.getTargets(), game)); - } - - if (newTargetDescription.length() > 0 && !game.isSimulation()) { - game.informPlayers(this.getName() + " is now " + newTargetDescription.toString()); - } - return true; - } - return false; + public String getTargetDescription(Targets targets, Game game) { + return getAbilities().get(0).getTargetDescription(targets, game); } - /** - * Handles the change of one target instance of a mode - * - * @param player - player that can choose the new target - * @param ability - * @param mode - * @param target - * @param forceChange - * @param game - * @return - */ - private Target chooseNewTarget(Player player, Ability ability, Mode mode, Target target, boolean forceChange, FilterPermanent filterNewTarget, Game game) { - Target newTarget = target.copy(); - newTarget.clearChosen(); - for (UUID targetId : target.getTargets()) { - String targetNames = getNamesOfTargets(targetId, game); - // change the target? - if (targetNames != null - && (forceChange || player.chooseUse(mode.getEffects().get(0).getOutcome(), "Change this target: " + targetNames + "?", game))) { - // choose exactly one other target - if (forceChange && target.possibleTargets(this.getSourceId(), getControllerId(), game).size() > 1) { // controller of ability must be used (e.g. TargetOpponent) - int iteration = 0; - do { - if (iteration > 0 && !game.isSimulation()) { - game.informPlayer(player, "You may only select exactly one target that must be different from the origin target!"); - } - iteration++; - newTarget.clearChosen(); - // TODO: Distinction between "ability controller" and "player that can change the target" - here player is used for both - newTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), ability, game); - // check target restriction - if (newTarget.getFirstTarget() != null && filterNewTarget != null) { - Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); - if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { - game.informPlayer(player, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")"); - newTarget.clearChosen(); - } - } - } while (player.isInGame() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1)); - // choose a new target - } else { - // build a target definition with exactly one possible target to select that replaces old target - Target tempTarget = target.copy(); - if (target instanceof TargetAmount) { - ((TargetAmount)tempTarget).setAmountDefinition(new StaticValue(target.getTargetAmount(targetId))); - } - tempTarget.setMinNumberOfTargets(1); - tempTarget.setMaxNumberOfTargets(1); - boolean again; - do { - again = false; - tempTarget.clearChosen(); - if (!tempTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), ability, game)) { - if (player.chooseUse(Outcome.Benefit, "No target object selected. Reset to original target?", game)) { - // use previous target no target was selected - newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); - } else { - again = true; - } - } else { - // if possible add the alternate Target - it may not be included in the old definition nor in the already selected targets of the new definition - if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) { - if (player.isHuman()) { - game.informPlayer(player, "This target was already selected from origin ability. You can only keep this target!"); - again = true; - } else { - newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); - } - } else if (newTarget.getFirstTarget() != null && filterNewTarget != null) { - Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); - if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { - game.informPlayer(player, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")"); - again = true; - } - } else { - // valid target was selected, add it to the new target definition - newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, false); - } - } - } while (again && player.isInGame()); - } - } - // keep the target - else { - newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); - } - } - return newTarget; - } - - - private String getNamesOfTargets(UUID targetId, Game game) { - MageObject object = game.getObject(targetId); - String targetNames = null; - if (object == null) { - Player targetPlayer = game.getPlayer(targetId); - if (targetPlayer != null) { - targetNames = targetPlayer.getLogName(); - } - } else { - targetNames = object.getName(); - } - return targetNames; - } - } diff --git a/Mage/src/mage/game/stack/StackObjImpl.java b/Mage/src/mage/game/stack/StackObjImpl.java new file mode 100644 index 00000000000..c4889dc1c90 --- /dev/null +++ b/Mage/src/mage/game/stack/StackObjImpl.java @@ -0,0 +1,260 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.game.stack; + +import java.util.Set; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Abilities; +import mage.abilities.AbilitiesImpl; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetAmount; + +/** + * + * @author LevelX2 + */ +public abstract class StackObjImpl implements StackObject { + + /** + * Choose new targets for a stack Object + * + * @param game + * @param playerId Player UUID who changes the targets. + * @return + */ + public boolean chooseNewTargets(Game game, UUID playerId) { + return chooseNewTargets(game, playerId, false, false, null); + } + + /** + * 114.6. Some effects allow a player to change the target(s) of a spell or + * ability, and other effects allow a player to choose new targets for a + * spell or ability. + * + * 114.6a If an effect allows a player to "change the + * target(s)" of a spell or ability, each target can be changed only to + * another legal target. If a target can't be changed to another legal + * target, the original target is unchanged, even if the original target is + * itself illegal by then. If all the targets aren't changed to other legal + * targets, none of them are changed. + * + * 114.6b If an effect allows a player to "change a target" of a + * spell or ability, the process described in rule 114.6a + * is followed, except that only one of those targets may be changed + * (rather than all of them or none of them). + * + * 114.6c If an effect allows a + * player to "change any targets" of a spell or ability, the process + * described in rule 114.6a is followed, except that any number of those + * targets may be changed (rather than all of them or none of them). + * + * 114.6d If an effect allows a player to "choose new targets" for a spell or + * ability, the player may leave any number of the targets unchanged, even + * if those targets would be illegal. If the player chooses to change some + * or all of the targets, the new targets must be legal and must not cause + * any unchanged targets to become illegal. + * + * 114.6e When changing targets or + * choosing new targets for a spell or ability, only the final set of + * targets is evaluated to determine whether the change is legal. + * + * Example: Arc Trail is a sorcery that reads "Arc Trail deals 2 damage to + * target creature or player and 1 damage to another target creature or + * player." The current targets of Arc Trail are Runeclaw Bear and Llanowar + * Elves, in that order. You cast Redirect, an instant that reads "You may + * choose new targets for target spell," targeting Arc Trail. You can change + * the first target to Llanowar Elves and change the second target to + * Runeclaw Bear. + * + * 114.7. Modal spells and abilities may have different targeting + * requirements for each mode. An effect that allows a player to change the + * target(s) of a modal spell or ability, or to choose new targets for a + * modal spell or ability, doesn't allow that player to change its mode. + * (See rule 700.2.) + * + * 706.10c Some effects copy a spell or ability and state that its + * controller may choose new targets for the copy. The player may leave any + * number of the targets unchanged, even if those targets would be illegal. + * If the player chooses to change some or all of the targets, the new + * targets must be legal. Once the player has decided what the copy's + * targets will be, the copy is put onto the stack with those targets. + * + * @param game + * @param targetControllerId - player that can/has to change the target of the spell + * @param forceChange - does only work for targets with maximum of one targetId + * @param onlyOneTarget - 114.6b one target must be changed to another target + * @param filterNewTarget restriction for the new target, if null nothing is cheched + * @return + */ + @Override + public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { + Player targetController = game.getPlayer(targetControllerId); + if (targetController != null) { + StringBuilder newTargetDescription = new StringBuilder(); + // Fused split spells or spells where "Splice on Arcane" was used can have more than one ability + Abilities objectAbilities = new AbilitiesImpl<>(); + if (this instanceof Spell) { + objectAbilities.addAll(((Spell)this).getSpellAbilities()); + } else { + objectAbilities.addAll(getAbilities()); + } + for (Ability ability : objectAbilities) { + // Some spells can have more than one mode + for (UUID modeId : ability.getModes().getSelectedModes()) { + Mode mode = ability.getModes().get(modeId); + for (Target target : mode.getTargets()) { + Target newTarget = chooseNewTarget(targetController, ability, mode, target, forceChange, filterNewTarget, game); + // clear the old target and copy all targets from new target + target.clearChosen(); + for (UUID targetId : newTarget.getTargets()) { + target.addTarget(targetId, newTarget.getTargetAmount(targetId), ability, game, false); + } + + } + newTargetDescription.append(ability.getTargetDescription(mode.getTargets(), game)); + } + + } + if (newTargetDescription.length() > 0 && !game.isSimulation()) { + game.informPlayers(this.getLogName() + " is now " + newTargetDescription.toString()); + } + return true; + } + return false; + } + + /** + * Handles the change of one target instance of a mode + * + * @param targetController - player that can choose the new target + * @param ability + * @param mode + * @param target + * @param forceChange + * @param game + * @return + */ + private Target chooseNewTarget(Player targetController, Ability ability, Mode mode, Target target, boolean forceChange, FilterPermanent filterNewTarget, Game game) { + Target newTarget = target.copy(); + if (!targetController.getId().equals(getControllerId())) { + newTarget.setTargetController(targetController.getId()); // target controller for the change is different from spell controller + newTarget.setAbilityController(getControllerId()); + } + newTarget.clearChosen(); + for (UUID targetId : target.getTargets()) { + String targetNames = getNamesOftargets(targetId, game); + // change the target? + if (targetNames != null + && (forceChange || targetController.chooseUse(mode.getEffects().get(0).getOutcome(), "Change this target: " + targetNames + "?", game))) { + Set possibleTargets = target.possibleTargets(this.getSourceId(), getControllerId(), game); + // choose exactly one other target - already targeted objects are not counted + if (forceChange && possibleTargets != null && possibleTargets.size() > 1) { // controller of spell must be used (e.g. TargetOpponent) + int iteration = 0; + do { + if (iteration > 0 && !game.isSimulation()) { + game.informPlayer(targetController, "You may only select exactly one target that must be different from the origin target!"); + } + iteration++; + newTarget.clearChosen(); + + newTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), getControllerId(), ability, game); + // check target restriction + if (newTarget.getFirstTarget() != null && filterNewTarget != null) { + Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); + if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { + game.informPlayer(targetController, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")"); + newTarget.clearChosen(); + } + } + } while (targetController.isInGame() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1)); + // choose a new target + } else { + // build a target definition with exactly one possible target to select that replaces old target + Target tempTarget = target.copy(); + if (target instanceof TargetAmount) { + ((TargetAmount)tempTarget).setAmountDefinition(new StaticValue(target.getTargetAmount(targetId))); + } + tempTarget.setMinNumberOfTargets(1); + tempTarget.setMaxNumberOfTargets(1); + if (!targetController.getId().equals(getControllerId())) { + tempTarget.setTargetController(targetController.getId()); + tempTarget.setAbilityController(getControllerId()); + } + boolean again; + do { + again = false; + tempTarget.clearChosen(); + if (!tempTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), getControllerId(), ability, game)) { + if (targetController.chooseUse(Outcome.Benefit, "No target object selected. Reset to original target?", game)) { + // use previous target no target was selected + newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + } else { + again = true; + } + } else { + // if possible add the alternate Target - it may not be included in the old definition nor in the already selected targets of the new definition + if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) { + if(targetController.isHuman()) { + game.informPlayer(targetController, "This target was already selected from origin spell. You can only keep this target!"); + again = true; + } else { + newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + } + } else if (!target.canTarget(getControllerId(), tempTarget.getFirstTarget(), ability, game)) { + if(targetController.isHuman()) { + game.informPlayer(targetController, "This target is not valid!"); + again = true; + } else { + // keep the old + newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + } + } else if (newTarget.getFirstTarget() != null && filterNewTarget != null) { + Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); + if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { + game.informPlayer(targetController, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")"); + again = true; + } + } else { + // valid target was selected, add it to the new target definition + newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, false); + } + } + } while (again && targetController.isInGame()); + } + } + // keep the target + else { + newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + } + } + return newTarget; + } + + + private String getNamesOftargets(UUID targetId, Game game) { + MageObject object = game.getObject(targetId); + String name = null; + if (object == null) { + Player targetPlayer = game.getPlayer(targetId); + if (targetPlayer != null) { + name = targetPlayer.getLogName(); + } + } else { + name = object.getName(); + } + return name; + } + +} diff --git a/Mage/src/mage/game/turn/Phase.java b/Mage/src/mage/game/turn/Phase.java index 8881f21c1c1..0fc741fcc4f 100644 --- a/Mage/src/mage/game/turn/Phase.java +++ b/Mage/src/mage/game/turn/Phase.java @@ -113,6 +113,9 @@ public abstract class Phase implements Serializable { currentStep = step; if (!game.getState().getTurnMods().skipStep(activePlayerId, getStep().getType())) { playStep(game); + if (game.executingRollback()) { + return true; + } } if (!game.isSimulation() && checkStopOnStepOption(game)) { return false; @@ -201,6 +204,9 @@ public abstract class Phase implements Serializable { prePriority(game, activePlayerId); if (!game.isPaused() && !game.gameOver(null)) { currentStep.priority(game, activePlayerId, false); + if(game.executingRollback()) { + return; + } } if (!game.isPaused() && !game.gameOver(null)) { postPriority(game, activePlayerId); diff --git a/Mage/src/mage/game/turn/Turn.java b/Mage/src/mage/game/turn/Turn.java index da1b3b830f8..c781a711d2d 100644 --- a/Mage/src/mage/game/turn/Turn.java +++ b/Mage/src/mage/game/turn/Turn.java @@ -117,30 +117,34 @@ public class Turn implements Serializable { return null; } - public void play(Game game, UUID activePlayerId) { + public void play(Game game, Player activePlayer) { + activePlayer.becomesActivePlayer(); this.setDeclareAttackersStepStarted(false); if (game.isPaused() || game.gameOver(null)) { return; } - if (game.getState().getTurnMods().skipTurn(activePlayerId)) { + if (game.getState().getTurnMods().skipTurn(activePlayer.getId())) { return; } - checkTurnIsControlledByOtherPlayer(game, activePlayerId); + checkTurnIsControlledByOtherPlayer(game, activePlayer.getId()); - this.activePlayerId = activePlayerId; + this.activePlayerId = activePlayer.getId(); resetCounts(); - game.getPlayer(activePlayerId).beginTurn(game); + game.getPlayer(activePlayer.getId()).beginTurn(game); for (Phase phase: phases) { if (game.isPaused() || game.gameOver(null)) { return; } if (!isEndTurnRequested() || phase.getType().equals(TurnPhase.END)) { currentPhase = phase; - game.fireEvent(new GameEvent(GameEvent.EventType.PHASE_CHANGED, activePlayerId, null, activePlayerId)); - if (!game.getState().getTurnMods().skipPhase(activePlayerId, currentPhase.getType())) { - if (phase.play(game, activePlayerId)) { + game.fireEvent(new GameEvent(GameEvent.EventType.PHASE_CHANGED, activePlayer.getId(), null, activePlayer.getId())); + if (!game.getState().getTurnMods().skipPhase(activePlayer.getId(), currentPhase.getType())) { + if (phase.play(game, activePlayer.getId())) { + if(game.executingRollback()) { + return; + } //20091005 - 500.4/703.4n game.emptyManaPools(); game.saveState(false); diff --git a/Mage/src/mage/players/ManaPool.java b/Mage/src/mage/players/ManaPool.java index 3e50bbf9c24..200faae6b79 100644 --- a/Mage/src/mage/players/ManaPool.java +++ b/Mage/src/mage/players/ManaPool.java @@ -61,6 +61,7 @@ public class ManaPool implements Serializable { private final List manaItems = new ArrayList<>(); private boolean autoPayment; // auto payment from mana pool: true - mode is active + private boolean autoPaymentRestricted; // auto payment from mana pool: true - if auto Payment is on, it will only pay if one kind of mana is in the pool private ManaType unlockedManaType; // type of mana that was selected to pay manually private final Set doNotEmptyManaTypes = new HashSet<>(); @@ -68,6 +69,7 @@ public class ManaPool implements Serializable { public ManaPool(UUID playerId) { this.playerId = playerId; autoPayment = true; + autoPaymentRestricted = true; unlockedManaType = null; } @@ -77,6 +79,7 @@ public class ManaPool implements Serializable { manaItems.add(item.copy()); } this.autoPayment = pool.autoPayment; + this.autoPaymentRestricted = pool.autoPaymentRestricted; this.unlockedManaType = pool.unlockedManaType; this.doNotEmptyManaTypes.addAll(pool.doNotEmptyManaTypes); } @@ -101,11 +104,25 @@ public class ManaPool implements Serializable { return get(ManaType.BLACK); } + /** + * + * @param manaType the mana type that should be paid + * @param ability + * @param filter + * @param game + * @return + */ public boolean pay(ManaType manaType, Ability ability, Filter filter, Game game) { if (!autoPayment && !manaType.equals(unlockedManaType)) { // if manual payment and the needed mana type was not unlocked, nothing will be paid return false; } + if (autoPayment && autoPaymentRestricted && !wasManaAddedBeyondStock() && !manaType.equals(unlockedManaType)) { + // if automatic restricted payment and there is laready mana in the pool + // and the needed mana type was not unlocked, nothing will be paid + return false; + } + if (getConditional(manaType, ability, filter, game) > 0) { removeConditional(manaType, ability, game); lockManaType(); // pay only one mana if mana payment is set to manually @@ -118,6 +135,10 @@ public class ManaPool implements Serializable { continue; } } + if (!manaType.equals(unlockedManaType) && autoPayment && autoPaymentRestricted && mana.count() == mana.getStock()) { + // no mana added beyond the stock so don't auto pay this + continue; + } boolean spendAnyMana = spendAnyMana(ability, game); if (mana.get(manaType) > 0 || (spendAnyMana && mana.count() > 0)) { GameEvent event = new GameEvent(GameEvent.EventType.MANA_PAYED, ability.getId(), mana.getSourceId(), ability.getControllerId(), 0, mana.getFlag()); @@ -128,6 +149,9 @@ public class ManaPool implements Serializable { } else { mana.remove(manaType); } + if (mana.count() == 0) { // so no items with count 0 stay in list + manaItems.remove(mana); + } lockManaType(); // pay only one mana if mana payment is set to manually return true; } @@ -416,6 +440,14 @@ public class ManaPool implements Serializable { this.autoPayment = autoPayment; } + public void setAutoPaymentRestricted(boolean autoPaymentRestricted) { + this.autoPaymentRestricted = autoPaymentRestricted; + } + + public boolean isAutoPaymentRestricted() { + return autoPaymentRestricted; + } + public ManaType getUnlockedManaType() { return unlockedManaType; } @@ -428,4 +460,18 @@ public class ManaPool implements Serializable { this.unlockedManaType = null; } + public void setStock() { + for (ManaPoolItem mana : manaItems) { + mana.setStock(mana.count()); + } + } + + private boolean wasManaAddedBeyondStock() { + for (ManaPoolItem mana : manaItems) { + if (mana.getStock() < mana.count()) { + return true; + } + } + return false; + } } diff --git a/Mage/src/mage/players/ManaPoolItem.java b/Mage/src/mage/players/ManaPoolItem.java index 0d952cb41cd..b6a8f796b9a 100644 --- a/Mage/src/mage/players/ManaPoolItem.java +++ b/Mage/src/mage/players/ManaPoolItem.java @@ -51,6 +51,7 @@ public class ManaPoolItem implements Serializable { private UUID originalId; // originalId of the mana producing ability private boolean flag = false; private Duration duration; + private int stock; // amount the item had at the start of casting something public ManaPoolItem() {} @@ -91,6 +92,7 @@ public class ManaPoolItem implements Serializable { this.originalId = item.originalId; this.flag = item.flag; this.duration = item.duration; + this.stock = item.stock; } public ManaPoolItem copy() { @@ -207,8 +209,9 @@ public class ManaPoolItem implements Serializable { } public void removeAny() { + int oldCount = count(); if (black > 0) { - black--; + black--; } else if (blue > 0) { blue--; } else if (green > 0) { @@ -219,10 +222,14 @@ public class ManaPoolItem implements Serializable { white--; } else if (colorless > 0) { colorless--; - } + } + if (stock == oldCount && oldCount > count()) { + stock--; + } } public void remove(ManaType manaType) { + int oldCount = count(); switch(manaType) { case BLACK: if (black > 0) { @@ -255,6 +262,9 @@ public class ManaPoolItem implements Serializable { } break; } + if (stock == oldCount && oldCount > count()) { + stock--; + } } public void clear(ManaType manaType) { @@ -310,5 +320,13 @@ public class ManaPoolItem implements Serializable { public void setDuration(Duration duration) { this.duration = duration; } + + public int getStock() { + return stock; + } + + public void setStock(int stock) { + this.stock = stock; + } } diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index 0e9adf9bd0e..c2f4a016acd 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -49,6 +49,7 @@ import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.mana.ManaOptions; import mage.cards.Card; import mage.cards.Cards; import mage.cards.decks.Deck; @@ -280,6 +281,7 @@ public interface Player extends MageItem, Copyable { void leave(); void concede(Game game); void abort(); + void abortReset(); void skip(); // priority, undo, ... @@ -376,8 +378,10 @@ public interface Player extends MageItem, Copyable { void phasing(Game game); void untap(Game game); + ManaOptions getManaAvailable(Game game); List getPlayable(Game game, boolean hidden); List getPlayableOptions(Ability ability, Game game); + Set getPlayableInHand(Game game); LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game); @@ -385,7 +389,7 @@ public interface Player extends MageItem, Copyable { void addCounters(Counter counter, Game game); List getAttachments(); boolean addAttachment(UUID permanentId, Game game); - boolean removeAttachment(UUID permanentId, Game game); + boolean removeAttachment(Permanent permanent, Game game); /** * Signals that the player becomes active player in this turn. diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index f5cc42cb979..a9fb708017f 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -130,7 +130,6 @@ import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetDiscard; import mage.util.CardUtil; import mage.util.GameLog; -import mage.watchers.common.BloodthirstWatcher; import org.apache.log4j.Logger; @@ -370,8 +369,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.canPaySacrificeCost = player.canPaySacrificeCost(); this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife(); this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard(); - this.alternativeSourceCosts.addAll(player.getAlternativeSourceCosts()); - this.storedBookmark = player.getStoredBookmark(); + this.alternativeSourceCosts.addAll(player.getAlternativeSourceCosts()); this.topCardRevealed = player.isTopCardRevealed(); this.playersUnderYourControl.clear(); @@ -386,7 +384,9 @@ public abstract class PlayerImpl implements Player, Serializable { this.castSourceIdWithAlternateMana = player.getCastSourceIdWithAlternateMana(); this.castSourceIdManaCosts = player.getCastSourceIdManaCosts(); - this.usersAllowedToSeeHandCards.addAll(player.getUsersAllowedToSeeHandCards()); + // Don't restore! + // this.storedBookmark + // this.usersAllowedToSeeHandCards } @Override @@ -573,6 +573,10 @@ public abstract class PlayerImpl implements Player, Serializable { playersUnderYourControl.clear(); } + /** + * returns true if the player has the control itself - false if the player is controlled by another player + * @return + */ @Override public boolean isGameUnderControl() { return isGameUnderControl; @@ -690,7 +694,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public Cards discard(int amount, boolean random, Ability source, Game game) { Cards discardedCards = new CardsImpl(); - if (amount >= this.getHand().size()) { + if (this.getHand().size() == 1) { discardedCards.addAll(this.getHand()); while (this.getHand().size() > 0) { discard(this.getHand().get(this.getHand().iterator().next(), game), source, game); @@ -772,15 +776,12 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean removeAttachment(UUID permanentId, Game game) { - if (this.attachments.contains(permanentId)) { - Permanent aura = game.getPermanent(permanentId); - if (aura != null) { - if (!game.replaceEvent(new GameEvent(GameEvent.EventType.UNATTACH, playerId, permanentId, aura.getControllerId()))) { - this.attachments.remove(permanentId); - aura.attachTo(null, game); - } - game.fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, playerId, permanentId, aura.getControllerId())); + public boolean removeAttachment(Permanent attachment, Game game) { + if (this.attachments.contains(attachment.getId())) { + if (!game.replaceEvent(new GameEvent(GameEvent.EventType.UNATTACH, playerId, attachment.getId(), attachment.getControllerId()))) { + this.attachments.remove(attachment.getId()); + attachment.attachTo(null, game); + game.fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, playerId, attachment.getId(), attachment.getControllerId())); return true; } } @@ -795,7 +796,13 @@ public abstract class PlayerImpl implements Player, Serializable { Permanent attachedTo = game.getPermanent(permanent.getAttachedTo()); if (attachedTo != null) { attachedTo.removeAttachment(permanent.getId(), game); + } else { + Player attachedToPlayer = game.getPlayer(permanent.getAttachedTo()); + if (attachedToPlayer != null) { + attachedToPlayer.removeAttachment(permanent, game); + } } + } if (permanent.getPairedCard() != null) { Permanent pairedCard = game.getPermanent(permanent.getPairedCard()); @@ -1148,7 +1155,7 @@ public abstract class PlayerImpl implements Player, Serializable { return true; } } - game.restoreState(bookmark, source.getRule()); + game.restoreState(bookmark, source.getRule()); // why restore is needed here? return false; } @@ -1853,7 +1860,7 @@ public abstract class PlayerImpl implements Player, Serializable { passedAllTurns = false; passedUntilEndOfTurn = true; passedUntilStackResolved = false; - skippedAtLeastOnce = !game.getTurn().getStepType().equals(PhaseStep.END_TURN); + skippedAtLeastOnce = !PhaseStep.END_TURN.equals(game.getTurn().getStepType()); this.skip(); break; case PASS_PRIORITY_UNTIL_NEXT_TURN: // F4 @@ -2141,7 +2148,8 @@ public abstract class PlayerImpl implements Player, Serializable { return blockers; } - protected ManaOptions getManaAvailable(Game game) { + @Override + public ManaOptions getManaAvailable(Game game) { ManaOptions available = new ManaOptions(); List> sourceWithoutCosts = new ArrayList<>(); @@ -2199,7 +2207,7 @@ public abstract class PlayerImpl implements Player, Serializable { } // returns only mana producers that require mana payment - protected List getAvailableManaProducersWithCost(Game game) { + public List getAvailableManaProducersWithCost(Game game) { List result = new ArrayList<>(); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) { for (ManaAbility ability : permanent.getAbilities().getManaAbilities(Zone.BATTLEFIELD)) { @@ -2374,7 +2382,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (hidden) { for (Card card : hand.getUniqueCards(game)) { - for (Ability ability : card.getAbilities()) { // gets this activated ability from hand? (Morph?) + for (Ability ability : card.getAbilities(game)) { // gets this activated ability from hand? (Morph?) if (ability.getZone().match(Zone.HAND)) { if (ability instanceof ActivatedAbility) { if (!(ability instanceof PlayLandAbility) @@ -2454,7 +2462,7 @@ public abstract class PlayerImpl implements Player, Serializable { for (CommandObject commandObject : game.getState().getCommand()) { for (ActivatedAbility ability : commandObject.getAbilities().getActivatedAbilities(Zone.COMMAND)) { if (ability.getControllerId().equals(getId()) - && ability.getAbilityType().equals(AbilityType.ACTIVATED) + && ability instanceof ActivatedAbility && canPlay(ability, availableMana, game.getObject(ability.getSourceId()), game)) { playableActivated.put(ability.toString(), ability); } @@ -2901,6 +2909,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardsToGraveyardWithInfo(List allCards, Ability source, Game game, Zone fromZone) { + boolean result = true; + UUID sourceId = source == null ? null : source.getSourceId(); while (!allCards.isEmpty()) { // identify cards from one owner Cards cards = new CardsImpl(); @@ -2915,7 +2925,7 @@ public abstract class PlayerImpl implements Player, Serializable { cards.add(card); } } - // move cards ot graveyard in order the owner decides + // move cards to graveyard in order the owner decides if (!cards.isEmpty()) { Player choosingPlayer = this; if (ownerId != this.getId()) { @@ -2937,22 +2947,21 @@ public abstract class PlayerImpl implements Player, Serializable { Card card = cards.get(targetObjectId, game); cards.remove(targetObjectId); if (card != null) { - choosingPlayer.moveCardToGraveyardWithInfo(card, source.getSourceId(), game, fromZone); + result &= choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone); } target.clearChosen(); } if (cards.size() == 1) { - choosingPlayer.moveCardToGraveyardWithInfo(cards.getCards(game).iterator().next(), source == null ? null : source.getSourceId(), game, fromZone); + result &= choosingPlayer.moveCardToGraveyardWithInfo(cards.getCards(game).iterator().next(), sourceId, game, fromZone); } } else { for (Card card : cards.getCards(game)) { - choosingPlayer.moveCardToGraveyardWithInfo(card, source.getSourceId(), game, fromZone); + result &= choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone); } } - } - + } } - return true; + return result; } @Override @@ -3123,4 +3132,8 @@ public abstract class PlayerImpl implements Player, Serializable { return matchPlayer; } + @Override + public void abortReset() { + abort = false; + } } diff --git a/Mage/src/mage/players/net/UserData.java b/Mage/src/mage/players/net/UserData.java index fe2a2ce1649..72823bd53ea 100644 --- a/Mage/src/mage/players/net/UserData.java +++ b/Mage/src/mage/players/net/UserData.java @@ -13,14 +13,20 @@ public class UserData implements Serializable { protected int avatarId; protected boolean showAbilityPickerForced; protected boolean allowRequestShowHandCards; + protected boolean confirmEmptyManaPool; protected UserSkipPrioritySteps userSkipPrioritySteps; + protected String flagName; - public UserData(UserGroup userGroup, int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, UserSkipPrioritySteps userSkipPrioritySteps) { + public UserData(UserGroup userGroup, int avatarId, boolean showAbilityPickerForced, + boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps, + String flagName) { this.groupId = userGroup.getGroupId(); this.avatarId = avatarId; this.showAbilityPickerForced = showAbilityPickerForced; this.allowRequestShowHandCards = allowRequestShowHandCards; this.userSkipPrioritySteps = userSkipPrioritySteps; + this.confirmEmptyManaPool = confirmEmptyManaPool; + this.flagName = flagName; } public void setGroupId(int groupId) { @@ -66,5 +72,17 @@ public class UserData implements Serializable { public void setUserSkipPrioritySteps(UserSkipPrioritySteps userSkipPrioritySteps) { this.userSkipPrioritySteps = userSkipPrioritySteps; } + + public boolean confirmEmptyManaPool() { + return confirmEmptyManaPool; + } + + public void setConfirmEmptyManaPool(boolean confirmEmptyManaPool) { + this.confirmEmptyManaPool = confirmEmptyManaPool; + } + + public String getFlagName() { + return flagName; + } } diff --git a/Mage/src/mage/target/Target.java b/Mage/src/mage/target/Target.java index c1c5ea41846..0ac5a3c43ba 100644 --- a/Mage/src/mage/target/Target.java +++ b/Mage/src/mage/target/Target.java @@ -38,6 +38,7 @@ import java.io.Serializable; import java.util.List; import java.util.Set; import java.util.UUID; +import mage.players.Player; /** * @@ -108,4 +109,7 @@ public interface Target extends Serializable { // some targets are choosen from players that are not the controller of the ability (e.g. Pandemonium) void setTargetController(UUID playerId); UUID getTargetController(); + void setAbilityController(UUID playerId); + UUID getAbilityController(); + Player getTargetController(Game game, UUID playerId); } diff --git a/Mage/src/mage/target/TargetAmount.java b/Mage/src/mage/target/TargetAmount.java index e9060539e22..36cbb56ebc4 100644 --- a/Mage/src/mage/target/TargetAmount.java +++ b/Mage/src/mage/target/TargetAmount.java @@ -117,10 +117,9 @@ public abstract class TargetAmount extends TargetImpl { if (!amountWasSet) { setAmount(source, game); } - Player player = game.getPlayer(playerId); chosen = remainingAmount == 0; while (remainingAmount > 0) { - if (!player.chooseTargetAmount(outcome, this, source, game)) { + if (!getTargetController(game, playerId).chooseTargetAmount(outcome, this, source, game)) { return chosen; } chosen = remainingAmount == 0; diff --git a/Mage/src/mage/target/TargetImpl.java b/Mage/src/mage/target/TargetImpl.java index c6d46a94c66..ab3eb6ef68e 100644 --- a/Mage/src/mage/target/TargetImpl.java +++ b/Mage/src/mage/target/TargetImpl.java @@ -61,6 +61,7 @@ public abstract class TargetImpl implements Target { protected boolean notTarget = false; protected boolean atRandom = false; protected UUID targetController = null; // if null the ability controller is the targetController + protected UUID abilityController = null; // only used if target controller != ability controller @Override public abstract TargetImpl copy(); @@ -86,6 +87,7 @@ public abstract class TargetImpl implements Target { this.atRandom = target.atRandom; this.notTarget = target.notTarget; this.targetController = target.targetController; + this.abilityController = target.abilityController; } @Override @@ -110,6 +112,12 @@ public abstract class TargetImpl implements Target { @Override public String getMessage() { + String suffix = ""; + if (targetController != null) { + // Hint for the selecting player that the targets must be valid from the point of the ability controller + // e.g. select opponent text may be misleading otherwise + suffix = " (target controlling!)"; + } if (getMaxNumberOfTargets() != 1) { StringBuilder sb = new StringBuilder(); sb.append("Select ").append(targetName); @@ -118,12 +126,13 @@ public abstract class TargetImpl implements Target { } else { sb.append(" (").append(targets.size()).append(")"); } + sb.append(suffix); return sb.toString(); } if (targetName.startsWith("another") || targetName.startsWith("a ") || targetName.startsWith("an ")) { - return "Select " + targetName; + return "Select " + targetName + suffix; } - return "Select a " + targetName; + return "Select a " + targetName + suffix; } @Override @@ -298,7 +307,6 @@ public abstract class TargetImpl implements Target { @Override public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) { - Player player = game.getPlayer(playerId); while (!isChosen() && !doneChosing()) { chosen = targets.size() >= getNumberOfTargets(); if (isRandom()) { @@ -316,7 +324,7 @@ public abstract class TargetImpl implements Target { return chosen; } } else { - if (!player.chooseTarget(outcome, this, source, game)) { + if (!getTargetController(game, playerId).chooseTarget(outcome, this, source, game)) { return chosen; } } @@ -433,5 +441,23 @@ public abstract class TargetImpl implements Target { return targetController; } + @Override + public void setAbilityController(UUID playerId) { + this.abilityController = playerId; + } + @Override + public UUID getAbilityController() { + return abilityController; + } + + @Override + public Player getTargetController(Game game, UUID playerId) { + if (getTargetController() != null) { + return game.getPlayer(getTargetController()); + } else { + return game.getPlayer(playerId); + } + } + } diff --git a/Mage/src/mage/util/Copier.java b/Mage/src/mage/util/Copier.java index 613723ce17d..4830d61bf2c 100644 --- a/Mage/src/mage/util/Copier.java +++ b/Mage/src/mage/util/Copier.java @@ -38,6 +38,7 @@ import java.util.zip.GZIPOutputStream; /** * * @author BetaSteward_at_googlemail.com + * @param */ public class Copier { @@ -95,8 +96,7 @@ public class Copier { public T uncompressCopy(byte[] buffer) { T copy = null; - try { - ObjectInputStream in = new CopierObjectInputStream(loader, new GZIPInputStream(new ByteArrayInputStream(buffer))); + try (ObjectInputStream in = new CopierObjectInputStream(loader, new GZIPInputStream(new ByteArrayInputStream(buffer)))) { copy = (T) in.readObject(); } catch(IOException e) { diff --git a/Mage/src/mage/util/GameLog.java b/Mage/src/mage/util/GameLog.java index 27887668cd3..082178a1168 100644 --- a/Mage/src/mage/util/GameLog.java +++ b/Mage/src/mage/util/GameLog.java @@ -38,6 +38,8 @@ import mage.ObjectColor; public class GameLog { static final String LOG_COLOR_PLAYER = "#20B2AA"; // LightSeaGreen + static final String LOG_COLOR_PLAYER_REQUEST = "#D2691E"; // Chocolate + static final String LOG_COLOR_PLAYER_CONFIRM = "#D2691E"; // Chocolate static final String LOG_COLOR_GREEN = "#90EE90"; // LightGreen static final String LOG_COLOR_RED = "#FF6347"; // Tomato static final String LOG_COLOR_BLUE = "#87CEFA"; // LightSkyBlue @@ -54,7 +56,7 @@ public class GameLog { } public static String getColoredObjectName(MageObject mageObject) { - return "" + mageObject.getName() + " ["+mageObject.getId().toString().substring(0,3) + "]"; + return "" + mageObject.getName() + " ["+mageObject.getId().toString().substring(0,3) + "]"; } public static String getNeutralColoredText(String text) { @@ -64,6 +66,18 @@ public class GameLog { public static String getColoredPlayerName(String name) { return "" + name + ""; } + + public static String getPlayerRequestColoredText(String name) { + return "" + name + ""; + } + + public static String getPlayerConfirmColoredText(String name) { + return "" + name + ""; + } + + public static String getSmallSecondLineText(String text) { + return "
" + text + "
"; + } private static String getColorName(ObjectColor objectColor) { if (objectColor.isMulticolored()) { diff --git a/Mage/src/mage/util/functions/CopyTokenFunction.java b/Mage/src/mage/util/functions/CopyTokenFunction.java index 28e737801b1..001c8188202 100644 --- a/Mage/src/mage/util/functions/CopyTokenFunction.java +++ b/Mage/src/mage/util/functions/CopyTokenFunction.java @@ -83,7 +83,7 @@ public class CopyTokenFunction implements Function { } target.setName(sourceObj.getName()); - target.getColor().setColor(sourceObj.getColor()); + target.getColor(null).setColor(sourceObj.getColor(null)); target.getManaCost().clear(); target.getManaCost().add(sourceObj.getManaCost()); target.getCardType().clear(); diff --git a/Mage/src/mage/util/trace/TraceUtil.java b/Mage/src/mage/util/trace/TraceUtil.java index 7c413213b85..b4da8fb9850 100644 --- a/Mage/src/mage/util/trace/TraceUtil.java +++ b/Mage/src/mage/util/trace/TraceUtil.java @@ -59,7 +59,7 @@ public class TraceUtil { for (UUID blockerId : group.getBlockers()) { Permanent blocker = game.getPermanent(blockerId); if (blocker != null && !blocker.getCardType().contains(CardType.ARTIFACT) - && !attacker.getColor().shares(blocker.getColor())) { + && !attacker.getColor(game).shares(blocker.getColor(game))) { log.warn("Found creature with intimidate blocked by non artifact not sharing color creature"); traceCombat(game, attacker, blocker); } diff --git a/Mage/src/mage/watchers/Watcher.java b/Mage/src/mage/watchers/Watcher.java index 180b7aac839..e9d8520293c 100644 --- a/Mage/src/mage/watchers/Watcher.java +++ b/Mage/src/mage/watchers/Watcher.java @@ -92,7 +92,7 @@ public abstract class Watcher implements Serializable { public boolean conditionMet() { return condition; } - + public void reset() { condition = false; } diff --git a/Mage/src/mage/watchers/common/LandfallWatcher.java b/Mage/src/mage/watchers/common/LandfallWatcher.java index 3a0a659c6e4..f75d4c0d625 100644 --- a/Mage/src/mage/watchers/common/LandfallWatcher.java +++ b/Mage/src/mage/watchers/common/LandfallWatcher.java @@ -1,5 +1,8 @@ package mage.watchers.common; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; import mage.constants.CardType; import mage.constants.WatcherScope; import mage.game.Game; @@ -13,12 +16,15 @@ import mage.watchers.Watcher; */ public class LandfallWatcher extends Watcher { + Set playerPlayedLand = new HashSet<>(); + public LandfallWatcher() { - super("LandPlayed", WatcherScope.PLAYER); + super("LandPlayed", WatcherScope.GAME); } public LandfallWatcher(final LandfallWatcher watcher) { super(watcher); + playerPlayedLand.addAll(playerPlayedLand); } @Override @@ -28,15 +34,21 @@ public class LandfallWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (condition == true) { //no need to check - condition has already occured - return; - } if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent.getCardType().contains(CardType.LAND) && permanent.getControllerId().equals(this.controllerId)) { - condition = true; + if (permanent.getCardType().contains(CardType.LAND) && !playerPlayedLand.contains(event.getPlayerId())) { + playerPlayedLand.add(event.getPlayerId()); } } } + @Override + public void reset() { + playerPlayedLand.clear(); + super.reset(); + } + + public boolean landPlayed(UUID playerId) { + return playerPlayedLand.contains(playerId); + } } diff --git a/Mage/src/mage/watchers/common/PlayerLostLifeWatcher.java b/Mage/src/mage/watchers/common/PlayerLostLifeWatcher.java index ae1a8247fe5..26b16292c40 100644 --- a/Mage/src/mage/watchers/common/PlayerLostLifeWatcher.java +++ b/Mage/src/mage/watchers/common/PlayerLostLifeWatcher.java @@ -68,9 +68,9 @@ public class PlayerLostLifeWatcher extends Watcher { if (playerId != null) { Integer amount = amountOfLifeLostThisTurn.get(playerId); if (amount == null) { - amount = Integer.valueOf(event.getAmount()); + amount = event.getAmount(); } else { - amount = Integer.valueOf(amount + event.getAmount()); + amount = amount + event.getAmount(); } amountOfLifeLostThisTurn.put(playerId, amount); } @@ -80,7 +80,7 @@ public class PlayerLostLifeWatcher extends Watcher { public int getLiveLost(UUID playerId) { Integer amount = amountOfLifeLostThisTurn.get(playerId); if (amount != null) { - return amount.intValue(); + return amount; } return 0; } @@ -88,7 +88,7 @@ public class PlayerLostLifeWatcher extends Watcher { public int getLiveLostLastTurn(UUID playerId) { Integer amount = amountOfLifeLostLastTurn.get(playerId); if (amount != null) { - return amount.intValue(); + return amount; } return 0; } diff --git a/readme.md b/readme.md index 902013827b2..a7ee59046a3 100644 --- a/readme.md +++ b/readme.md @@ -1,8 +1,8 @@ # XMage - Magic, Another Game Engine -XMage allows you to play magic against one or more online players or computer opponents. It includes full rules enforcement for over **10,000** unique cards (nearly 20.000 counting all cards from different editions). Starting with Eventide, all regular sets have nearly all the cards implemented ([see here in detail which cards are implemented](http://ct-magefree.rhcloud.com/stats)). +XMage allows you to play Magic against one or more online players or computer opponents. It includes full rules enforcement for over **10,000** unique cards (nearly 20,000 counting all cards from different editions). Starting with Eventide, all regular sets have nearly all the cards implemented ([detailed overview](http://ct-magefree.rhcloud.com/stats)). -There are public servers where you can play XMage against other players. Apart from this, you can also host your own server to play against the AI and/or your friends. +There are public servers where you can play XMage against other players. You can also host your own server to play against the AI and/or your friends. You can visit the XMage forum [here](http://www.slightlymagic.net/forum/viewforum.php?f=70). @@ -10,25 +10,26 @@ You can visit the XMage forum [here](http://www.slightlymagic.net/forum/viewforu * Deck editor to build your desired decks. * There is a simple computer AI opponent available. * You can play either a two player duel or a multiplayer free-for-all game with up to 10 players. -* Commander format(also up to 10 players). +* Commander format (also up to 10 players). * Tiny Leaders duels. -* There are two tournament types supported witch can be played with up to 16 players: +* There are two tournament types supported, which can be played with up to 16 players: * Elimination or swiss type handling * Booster (also Cube) draft tournaments (4-16) * Sealed (also from Cube) tournaments (2-16) ## Installation -Install the XMage Launcher to download and install always the latest XMage release from [here](http://XMage.de). +Download and install the [latest XMage release](http://XMage.de). You will need to have the [Java Runtime Environment](http://java.com/en/) Version 7 or later. -Here you can find a log of the latest changes: [Release changes] (http://github.com/magefree/mage/wiki/Release-changes) - -(look here for more in detail description what to do: http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=13632) +Look [here](http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=13632) for more detailed instructions. +[Here](http://github.com/magefree/mage/wiki/Release-changes) you can find a log of the latest changes. ## Developer -If you are interesting in developing XMage here are a couple of links that might interest you: +If you are interested in developing XMage, here are some useful resources: * [Developer Getting Started](http://github.com/magefree/mage/wiki/Developer-Getting-Started) * [Developer Notes](http://github.com/magefree/mage/wiki/Developer-Notes) -* [Developer Testing Tools] (http://github.com/magefree/mage/wiki/Developer-Testing-Tools) +* [Developer Testing Tools](http://github.com/magefree/mage/wiki/Developer-Testing-Tools) * [Double Faced Cards](http://github.com/magefree/mage/wiki/Double-Faced-Cards) +* [Card Requests](http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=4554) +* [Tournament Relevant Card Requests](http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=14062) \ No newline at end of file