diff --git a/.gitignore b/.gitignore index 92368d784db..d29c7c870ba 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ syntax: glob Mage.Client/*.dck Mage.Client/db Mage.Client/gamelogs +Mage.Client/gamelogsJson Mage.Client/*.log Mage.Client/plugins/images Mage.Client/plugins/plugin.data @@ -135,3 +136,4 @@ client_secrets.json dependency-reduced-pom.xml mage-bundle +/Mage.Client/game-*.json diff --git a/Mage.Client/pom.xml b/Mage.Client/pom.xml index db453f4cd3b..fd907aa729d 100644 --- a/Mage.Client/pom.xml +++ b/Mage.Client/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.27 + 1.4.28 org.mage @@ -15,6 +15,7 @@ Mage Client + org.mage mage @@ -68,6 +69,11 @@ jetlang 0.2.9 + + com.amazonaws + aws-java-sdk-s3 + 1.11.286 + com.jgoodies forms diff --git a/Mage.Client/src/main/java/mage/client/SessionHandler.java b/Mage.Client/src/main/java/mage/client/SessionHandler.java index 97a183c3cad..8524b125dae 100644 --- a/Mage.Client/src/main/java/mage/client/SessionHandler.java +++ b/Mage.Client/src/main/java/mage/client/SessionHandler.java @@ -1,7 +1,9 @@ package mage.client; +import java.util.*; import mage.cards.decks.DeckCardLists; import mage.client.chat.LocalCommands; +import mage.client.dialog.PreferencesDialog; import mage.constants.ManaType; import mage.constants.PlayerAction; import mage.game.match.MatchOptions; @@ -14,8 +16,6 @@ import mage.remote.Session; import mage.remote.SessionImpl; import mage.view.*; -import java.util.*; - /** * Created by IGOUDT on 15-9-2016. */ @@ -26,8 +26,9 @@ public final class SessionHandler { public static void startSession(MageFrame mageFrame) { session = new SessionImpl(mageFrame); + session.setJsonLogActive("true".equals(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_LOG_AUTO_SAVE, "true"))); } - + public static void ping() { session.ping(); } @@ -322,7 +323,7 @@ public final class SessionHandler { } public static void updateDeck(UUID tableId, DeckCardLists deckCardLists) { - session.updateDeck(tableId, deckCardLists); + session.updateDeck(tableId, deckCardLists); } public static boolean emailAuthToken(Connection connection) { @@ -330,10 +331,10 @@ public final class SessionHandler { } public static boolean resetPassword(Connection connection) { - return session.resetPassword(connection); + return session.resetPassword(connection); } public static boolean register(Connection connection) { - return session.register(connection); + return session.register(connection); } } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form index 17513f7a0da..0ea54a62d3b 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form @@ -853,7 +853,7 @@ - + @@ -871,7 +871,6 @@ - diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java index f6fc566cef3..d9d34876065 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java @@ -51,6 +51,10 @@ import mage.client.MageFrame; import mage.client.cards.*; import mage.client.constants.Constants.SortBy; import mage.client.deckeditor.table.TableModel; +import static mage.client.dialog.PreferencesDialog.KEY_DECK_EDITOR_SEARCH_NAMES; +import static mage.client.dialog.PreferencesDialog.KEY_DECK_EDITOR_SEARCH_RULES; +import static mage.client.dialog.PreferencesDialog.KEY_DECK_EDITOR_SEARCH_TYPES; +import static mage.client.dialog.PreferencesDialog.KEY_DECK_EDITOR_SEARCH_UNIQUE; import mage.client.util.GUISizeHelper; import mage.client.util.gui.FastSearchUtil; import mage.client.util.sets.ConstructedFormats; @@ -144,9 +148,11 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene mainTable.setOpaque(false); cbSortBy.setEnabled(false); chkPiles.setEnabled(false); -// chkNames.setEnabled(true); -// chkTypes.setEnabled(true); -// chkRules.setEnabled(true); + + chkNames.setSelected("true".equals(MageFrame.getPreferences().get(KEY_DECK_EDITOR_SEARCH_NAMES, "true"))); + chkTypes.setSelected("true".equals(MageFrame.getPreferences().get(KEY_DECK_EDITOR_SEARCH_TYPES, "true"))); + chkRules.setSelected("true".equals(MageFrame.getPreferences().get(KEY_DECK_EDITOR_SEARCH_RULES, "true"))); + chkUnique.setSelected("true".equals(MageFrame.getPreferences().get(KEY_DECK_EDITOR_SEARCH_UNIQUE, "true"))); mainTable.addMouseListener(new MouseAdapter() { @Override @@ -172,6 +178,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene public void cleanUp() { this.cardGrid.clear(); this.mainModel.clear(); + MageFrame.getPreferences().put(KEY_DECK_EDITOR_SEARCH_NAMES, Boolean.toString(chkNames.isSelected())); + MageFrame.getPreferences().put(KEY_DECK_EDITOR_SEARCH_RULES, Boolean.toString(chkRules.isSelected())); + MageFrame.getPreferences().put(KEY_DECK_EDITOR_SEARCH_TYPES, Boolean.toString(chkTypes.isSelected())); + MageFrame.getPreferences().put(KEY_DECK_EDITOR_SEARCH_UNIQUE, Boolean.toString(chkUnique.isSelected())); } public void changeGUISize() { @@ -413,12 +423,12 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene try { java.util.List filteredCards = new ArrayList<>(); setCursor(new Cursor(Cursor.WAIT_CURSOR)); - + boolean chkPD = chkPennyDreadful.isSelected(); if (chkPD) { generatePennyDreadfulHash(); } - + if (limited) { for (Card card : cards) { if (filter.match(card, null)) { @@ -1063,10 +1073,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene chkRulesActionPerformed(evt); } }); - + chkUnique.setSelected(true); chkUnique.setText("Unique"); - chkUnique.setToolTipText("Singleton results only."); + chkUnique.setToolTipText("Show only the first found card of every card name."); chkUnique.setFocusable(false); chkUnique.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT); chkUnique.setMaximumSize(new java.awt.Dimension(69, 16)); @@ -1079,7 +1089,6 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene } }); - jButtonSearch.setText("Search"); jButtonSearch.setToolTipText("Performs the search."); jButtonSearch.addActionListener(new java.awt.event.ActionListener() { @@ -1126,7 +1135,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene .addComponent(chkTypes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(chkRules, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGap(5, 5, 5) .addComponent(chkUnique, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(5, 5, 5) .addComponent(cardCountLabel) @@ -1357,9 +1366,9 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene // TODO add your handling code here: }//GEN-LAST:event_chkTypesActionPerformed - private void chkRulesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkRulesActionPerformed + private void chkRulesActionPerformed(java.awt.event.ActionEvent evt) { // TODO add your handling code here: - }//GEN-LAST:event_chkRulesActionPerformed + } private void chkUniqueActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkRulesActionPerformed // TODO add your handling code here: @@ -1442,8 +1451,8 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene private javax.swing.JCheckBox chkPennyDreadful; private javax.swing.JCheckBox chkPiles; private javax.swing.JCheckBox chkRules; - private javax.swing.JCheckBox chkUnique; private javax.swing.JCheckBox chkTypes; + private javax.swing.JCheckBox chkUnique; private javax.swing.JButton jButtonAddToMain; private javax.swing.JButton jButtonAddToSideboard; private javax.swing.JButton jButtonClean; 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 85bb3611aa5..6db932f598b 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.form @@ -30,7 +30,7 @@ - + @@ -40,31 +40,30 @@ - + + - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - @@ -74,7 +73,7 @@ - + @@ -102,12 +101,13 @@ + + + - - @@ -238,5 +238,11 @@ + + + + + + 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 fe58d3c612a..792c4dd9014 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java @@ -36,6 +36,7 @@ import javax.swing.DefaultComboBoxModel; import javax.swing.JLayeredPane; import mage.Mana; import mage.cards.Card; +import mage.cards.FrameStyle; import mage.cards.decks.Deck; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; @@ -134,7 +135,7 @@ public class AddLandDialog extends MageDialog { this.setVisible(true); } - private void addLands(String landName, int number) { + private void addLands(String landName, int number, boolean useFullArt) { String landSetName = (String) cbLandSet.getSelectedItem(); CardCriteria criteria = new CardCriteria(); @@ -157,9 +158,28 @@ public class AddLandDialog extends MageDialog { cards = CardRepository.instance.findCards(criteria); } - for (int i = 0; i < number; i++) { + int foundLands = 0; + int foundNoneAfter = 0; + for (int i = 0; foundLands != number && foundNoneAfter < 1000; i++) { Card land = cards.get(RandomUtil.nextInt(cards.size())).getMockCard(); - deck.getCards().add(land); + boolean useLand = !useFullArt; + if (useFullArt && (land.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC + || land.getFrameStyle() == FrameStyle.UGL_FULL_ART_BASIC + || land.getFrameStyle() == FrameStyle.UNH_FULL_ART_BASIC + || land.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC)) { + useLand = true; + } + if (useLand) { + deck.getCards().add(land); + foundLands++; + foundNoneAfter = 0; + } else { + foundNoneAfter++; + } + } + + if (foundNoneAfter >= 1000 && useFullArt) { + MageFrame.getInstance().showMessage("Unable to add enough " + landName + "s. You encountered an error in adding chosen lands. Unable to find enough full art lands in the set " + landSetName + "."); } } @@ -190,6 +210,7 @@ public class AddLandDialog extends MageDialog { panelSet = new javax.swing.JPanel(); cbLandSet = new javax.swing.JComboBox(); btnSetFastSearch = new javax.swing.JButton(); + ckbFullArtLands = new javax.swing.JCheckBox(); jButton2.setText("jButton2"); @@ -255,12 +276,15 @@ public class AddLandDialog extends MageDialog { }); panelSet.add(btnSetFastSearch); + ckbFullArtLands.setText("Only use full art lands"); + ckbFullArtLands.setToolTipText("For example, lands from ZEN/UST/HOU"); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addContainerGap() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblMountain) .addComponent(lblForest, javax.swing.GroupLayout.Alignment.TRAILING) @@ -269,25 +293,24 @@ public class AddLandDialog extends MageDialog { .addComponent(lblPains, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(lblSwamp, javax.swing.GroupLayout.Alignment.TRAILING)) .addGap(18, 18, 18) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(ckbFullArtLands) .addGroup(layout.createSequentialGroup() - .addComponent(btnAutoAdd, javax.swing.GroupLayout.DEFAULT_SIZE, 122, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(btnAutoAdd, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(spnMountain, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) + .addComponent(spnIsland, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) + .addComponent(spnForest, javax.swing.GroupLayout.Alignment.LEADING)) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(spnSwamp, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) + .addComponent(spnPlains, javax.swing.GroupLayout.Alignment.LEADING)))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnAdd) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnCancel)) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(spnMountain, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) - .addComponent(spnIsland, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) - .addComponent(spnForest, javax.swing.GroupLayout.Alignment.LEADING)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(spnSwamp, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) - .addComponent(spnPlains, javax.swing.GroupLayout.Alignment.LEADING))) - .addGap(0, 0, Short.MAX_VALUE)) - .addComponent(panelSet, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addContainerGap()) + .addComponent(panelSet, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -295,7 +318,7 @@ public class AddLandDialog extends MageDialog { .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblLandSet) - .addComponent(panelSet, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(panelSet, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(lblForest) @@ -317,11 +340,12 @@ public class AddLandDialog extends MageDialog { .addComponent(lblSwamp) .addComponent(spnSwamp, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(ckbFullArtLands) + .addGap(2, 2, 2) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(btnAutoAdd) .addComponent(btnAdd) - .addComponent(btnCancel) - .addComponent(btnAutoAdd)) - .addContainerGap()) + .addComponent(btnCancel))) ); pack(); @@ -337,12 +361,13 @@ public class AddLandDialog extends MageDialog { int nMountain = ((Number) spnMountain.getValue()).intValue(); int nPlains = ((Number) spnPlains.getValue()).intValue(); int nSwamp = ((Number) spnSwamp.getValue()).intValue(); + boolean useFullArt = ckbFullArtLands.isSelected(); - addLands("Forest", nForest); - addLands("Island", nIsland); - addLands("Mountain", nMountain); - addLands("Plains", nPlains); - addLands("Swamp", nSwamp); + addLands("Forest", nForest, useFullArt); + addLands("Island", nIsland, useFullArt); + addLands("Mountain", nMountain, useFullArt); + addLands("Plains", nPlains, useFullArt); + addLands("Swamp", nSwamp, useFullArt); this.removeDialog(); }//GEN-LAST:event_btnAddActionPerformed @@ -400,6 +425,7 @@ public class AddLandDialog extends MageDialog { private javax.swing.JButton btnCancel; private javax.swing.JButton btnSetFastSearch; private javax.swing.JComboBox cbLandSet; + private javax.swing.JCheckBox ckbFullArtLands; private javax.swing.JButton jButton2; private javax.swing.JLabel lblForest; private javax.swing.JLabel lblIsland; 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 a5a176504e9..ff7ddd68459 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form @@ -41,6 +41,9 @@ + + + @@ -152,6 +155,7 @@ + @@ -274,6 +278,13 @@ + + + + + + + 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 10413c67872..337475674ed 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 { lblGameType = new javax.swing.JLabel(); cbGameType = new javax.swing.JComboBox(); chkRollbackTurnsAllowed = new javax.swing.JCheckBox(); + chkSpectatorsAllowed = new javax.swing.JCheckBox(); chkRated = new javax.swing.JCheckBox(); lblFreeMulligans = new javax.swing.JLabel(); spnFreeMulligans = new javax.swing.JSpinner(); @@ -151,6 +152,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"); + chkSpectatorsAllowed.setText("Allow Spectators"); + chkSpectatorsAllowed.setToolTipText("Allow spectators to watch.\n"); + chkRated.setText("Rated"); chkRated.setToolTipText("Indicates if matches will be rated."); @@ -231,7 +235,9 @@ public class NewTableDialog extends MageDialog { .addGap(13, 13, 13) .addComponent(lblFreeMulligans) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(13, 13, 13) + .addComponent(chkSpectatorsAllowed)) .addGroup(layout.createSequentialGroup() .addComponent(txtName, javax.swing.GroupLayout.PREFERRED_SIZE, 178, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -325,7 +331,8 @@ public class NewTableDialog extends MageDialog { .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(chkRollbackTurnsAllowed)) + .addComponent(chkRollbackTurnsAllowed) + .addComponent(chkSpectatorsAllowed)) .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))) @@ -401,6 +408,7 @@ public class NewTableDialog extends MageDialog { options.setRange((RangeOfInfluence) this.cbRange.getSelectedItem()); options.setWinsNeeded((Integer) this.spnNumWins.getValue()); options.setRollbackTurnsAllowed(chkRollbackTurnsAllowed.isSelected()); + options.setSpectatorsAllowed(chkSpectatorsAllowed.isSelected()); options.setRated(chkRated.isSelected()); options.setFreeMulligans((Integer) this.spnFreeMulligans.getValue()); options.setPassword(this.txtPassword.getText()); @@ -658,6 +666,7 @@ public class NewTableDialog extends MageDialog { } this.spnNumWins.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_WINS + versionStr, "2"))); this.chkRollbackTurnsAllowed.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_ROLLBACK_TURNS_ALLOWED + versionStr, "Yes").equals("Yes")); + this.chkSpectatorsAllowed.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_SPECTATORS_ALLOWED + versionStr, "Yes").equals("Yes")); this.chkRated.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_RATED + versionStr, "No").equals("Yes")); this.spnFreeMulligans.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS + versionStr, "0"))); @@ -739,6 +748,7 @@ public class NewTableDialog extends MageDialog { private javax.swing.JComboBox cbSkillLevel; private javax.swing.JComboBox cbTimeLimit; private javax.swing.JCheckBox chkRollbackTurnsAllowed; + private javax.swing.JCheckBox chkSpectatorsAllowed; private javax.swing.JCheckBox chkRated; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; 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 1058a968e7b..fc3c45de371 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form @@ -25,17 +25,18 @@ - - - + + + + + + + + + - - - - - @@ -94,10 +95,10 @@ - + - + @@ -121,9 +122,10 @@ - - - + + + + @@ -134,7 +136,10 @@ - + + + + @@ -143,7 +148,7 @@ - + @@ -153,13 +158,23 @@ - + + + + + + + + + + + @@ -221,7 +236,7 @@ - +
@@ -252,7 +267,7 @@ - + @@ -289,7 +304,7 @@ - + @@ -4318,7 +4333,7 @@ - + @@ -5743,7 +5758,7 @@ - + @@ -6008,7 +6023,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 831900e297f..ba94fc5d2f9 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -65,12 +65,11 @@ import javax.swing.filechooser.FileFilter; import mage.client.MageFrame; import mage.client.SessionHandler; import mage.client.components.KeyBindButton; +import static mage.client.constants.Constants.BATTLEFIELD_FEEDBACK_COLORIZING_MODE_ENABLE_BY_MULTICOLOR; import mage.client.util.Config; import mage.client.util.GUISizeHelper; import mage.client.util.ImageHelper; import mage.client.util.gui.BufferedImageBuilder; - -import static mage.client.constants.Constants.BATTLEFIELD_FEEDBACK_COLORIZING_MODE_ENABLE_BY_MULTICOLOR; import static mage.constants.Constants.DEFAULT_AVATAR_ID; import static mage.constants.Constants.MAX_AVATAR_ID; import static mage.constants.Constants.MIN_AVATAR_ID; @@ -79,6 +78,7 @@ import mage.players.net.UserGroup; import mage.players.net.UserSkipPrioritySteps; import mage.remote.Connection; import mage.remote.Connection.ProxyType; +import mage.remote.Session; import mage.view.UserRequestMessage; import org.apache.log4j.Logger; @@ -122,6 +122,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_GAME_LOG_AUTO_SAVE = "gameLogAutoSave"; public static final String KEY_DRAFT_LOG_AUTO_SAVE = "draftLogAutoSave"; + public static final String KEY_JSON_GAME_LOG_AUTO_SAVE = "gameLogJsonAutoSave"; public static final String KEY_CARD_IMAGES_USE_DEFAULT = "cardImagesUseDefault"; public static final String KEY_CARD_IMAGES_PATH = "cardImagesPath"; @@ -191,6 +192,11 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_DECK_EDITOR_LAST_SORT = "deckEditorLastSort"; public static final String KEY_DECK_EDITOR_LAST_SEPARATE_CREATURES = "deckEditorLastSeparateCreatures"; + public static final String KEY_DECK_EDITOR_SEARCH_NAMES = "deckEditorSearchNames"; + public static final String KEY_DECK_EDITOR_SEARCH_TYPES = "deckEditorSearchTypes"; + public static final String KEY_DECK_EDITOR_SEARCH_RULES = "deckEditorSearchRules"; + public static final String KEY_DECK_EDITOR_SEARCH_UNIQUE = "deckEditorSearchUnique"; + // positions of divider bars public static final String KEY_TABLES_DIVIDER_LOCATION_1 = "tablePanelDividerLocation1"; public static final String KEY_TABLES_DIVIDER_LOCATION_2 = "tablePanelDividerLocation2"; @@ -229,6 +235,7 @@ public class PreferencesDialog extends javax.swing.JDialog { 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_SPECTATORS_ALLOWED = "newTableSpectatorsAllowed"; 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"; @@ -405,6 +412,7 @@ public class PreferencesDialog extends javax.swing.JDialog { main_gamelog = new javax.swing.JPanel(); cbGameLogAutoSave = new javax.swing.JCheckBox(); cbDraftLogAutoSave = new javax.swing.JCheckBox(); + cbGameJsonLogAutoSave = new javax.swing.JCheckBox(); main_card = new javax.swing.JPanel(); showCardName = new javax.swing.JCheckBox(); tooltipDelayLabel = new javax.swing.JLabel(); @@ -599,7 +607,7 @@ public class PreferencesDialog extends javax.swing.JDialog { main_gamelog.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Game log")); cbGameLogAutoSave.setSelected(true); - cbGameLogAutoSave.setText("Auto save game logs (to \"../Mage.Client/gamelogs/\" directory)"); + cbGameLogAutoSave.setText("Save game logs (to \"../Mage.Client/gamelogs/\" directory)"); cbGameLogAutoSave.setToolTipText("The logs of all your games will be saved to the mentioned folder if this option is switched on."); cbGameLogAutoSave.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -608,7 +616,7 @@ public class PreferencesDialog extends javax.swing.JDialog { }); cbDraftLogAutoSave.setSelected(true); - cbDraftLogAutoSave.setText("Auto save draft logs (to \"../Mage.Client/gamelogs/\" directory)"); + cbDraftLogAutoSave.setText("Save draft logs (to \"../Mage.Client/gamelogs/\" directory)"); cbDraftLogAutoSave.setToolTipText("The logs of all your games will be saved to the mentioned folder if this option is switched on."); cbDraftLogAutoSave.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -616,15 +624,25 @@ public class PreferencesDialog extends javax.swing.JDialog { } }); + cbGameJsonLogAutoSave.setSelected(true); + cbGameJsonLogAutoSave.setText("Save JSON game logs (to \"../Mage.Client/gamelogsJson/\" directory)"); + cbGameJsonLogAutoSave.setToolTipText("The JSON logs of all your games will be saved to the mentioned folder if this option is switched on."); + cbGameJsonLogAutoSave.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cbGameJsonLogAutoSaveActionPerformed(evt); + } + }); + org.jdesktop.layout.GroupLayout main_gamelogLayout = new org.jdesktop.layout.GroupLayout(main_gamelog); main_gamelog.setLayout(main_gamelogLayout); main_gamelogLayout.setHorizontalGroup( main_gamelogLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(main_gamelogLayout.createSequentialGroup() .addContainerGap() - .add(main_gamelogLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING, false) - .add(cbDraftLogAutoSave, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(cbGameLogAutoSave, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .add(main_gamelogLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(cbDraftLogAutoSave) + .add(cbGameJsonLogAutoSave) + .add(cbGameLogAutoSave, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 505, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); main_gamelogLayout.setVerticalGroup( @@ -632,7 +650,10 @@ public class PreferencesDialog extends javax.swing.JDialog { .add(main_gamelogLayout.createSequentialGroup() .add(cbGameLogAutoSave) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(cbDraftLogAutoSave)) + .add(cbDraftLogAutoSave) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(cbGameJsonLogAutoSave) + .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); main_card.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Card")); @@ -784,7 +805,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .add(cbShowStormCounter, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(cbAskMoveToGraveOrder, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(showAbilityPickerForced, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addContainerGap(255, Short.MAX_VALUE)) + .addContainerGap(177, Short.MAX_VALUE)) ); main_gameLayout.setVerticalGroup( main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) @@ -859,10 +880,10 @@ public class PreferencesDialog extends javax.swing.JDialog { .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(main_game, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(main_gamelog, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(main_gamelog, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 107, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(main_battlefield, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addContainerGap(154, Short.MAX_VALUE)) + .addContainerGap(121, Short.MAX_VALUE)) ); main_card.getAccessibleContext().setAccessibleName("Game panel"); @@ -1581,7 +1602,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(cbNumberOfDownloadThreads, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 153, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))) .add(cbUseDefaultImageFolder)) - .add(0, 270, Short.MAX_VALUE))) + .add(0, 308, Short.MAX_VALUE))) .addContainerGap()) ); panelCardImagesLayout.setVerticalGroup( @@ -2386,7 +2407,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .add(connection_serversLayout.createSequentialGroup() .add(141, 141, 141) .add(jLabel17))) - .addContainerGap(198, Short.MAX_VALUE)) + .addContainerGap(201, Short.MAX_VALUE)) ); connection_serversLayout.setVerticalGroup( connection_serversLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) @@ -2625,7 +2646,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .add(keyEndStep, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(keyToggleRecordMacro, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(controlsDescriptionLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 441, Short.MAX_VALUE))) + .add(controlsDescriptionLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 478, Short.MAX_VALUE))) .addContainerGap()) ); tabControlsLayout.setVerticalGroup( @@ -2707,16 +2728,16 @@ public class PreferencesDialog extends javax.swing.JDialog { getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) - .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup() - .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(saveButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 100, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) - .add(exitButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 100, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .add(6, 6, 6)) .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup() .addContainerGap() - .add(tabsPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addContainerGap()) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING) + .add(org.jdesktop.layout.GroupLayout.LEADING, tabsPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(layout.createSequentialGroup() + .add(0, 0, Short.MAX_VALUE) + .add(saveButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 100, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(exitButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 100, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))) + .add(6, 6, 6)) ); layout.setVerticalGroup( layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) @@ -2748,6 +2769,7 @@ public class PreferencesDialog extends javax.swing.JDialog { save(prefs, dialog.cbAskMoveToGraveOrder, KEY_GAME_ASK_MOVE_TO_GRAVE_ORDER, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbGameLogAutoSave, KEY_GAME_LOG_AUTO_SAVE, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbDraftLogAutoSave, KEY_DRAFT_LOG_AUTO_SAVE, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.cbGameJsonLogAutoSave, KEY_JSON_GAME_LOG_AUTO_SAVE, "true", "false", UPDATE_CACHE_POLICY); String paramName = KEY_BATTLEFIELD_FEEDBACK_COLORIZING_MODE; int paramValue = dialog.cbBattlefieldFeedbackColorizingMode.getSelectedIndex(); @@ -3193,9 +3215,16 @@ public class PreferencesDialog extends javax.swing.JDialog { }//GEN-LAST:event_showFullImagePathActionPerformed private void cbBattlefieldFeedbackColorizingModeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbBattlefieldFeedbackColorizingModeActionPerformed - + }//GEN-LAST:event_cbBattlefieldFeedbackColorizingModeActionPerformed + private void cbGameJsonLogAutoSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbGameJsonLogAutoSaveActionPerformed + Session session = SessionHandler.getSession(); + if (session != null) { + session.setJsonLogActive(cbGameJsonLogAutoSave.isSelected()); + } + }//GEN-LAST:event_cbGameJsonLogAutoSaveActionPerformed + private void showProxySettings() { Connection.ProxyType proxyType = (Connection.ProxyType) cbProxyType.getSelectedItem(); switch (proxyType) { @@ -3310,6 +3339,7 @@ public class PreferencesDialog extends javax.swing.JDialog { load(prefs, dialog.cbGameLogAutoSave, KEY_GAME_LOG_AUTO_SAVE, "true"); load(prefs, dialog.cbDraftLogAutoSave, KEY_DRAFT_LOG_AUTO_SAVE, "true"); + load(prefs, dialog.cbGameJsonLogAutoSave, KEY_JSON_GAME_LOG_AUTO_SAVE, "true", "false"); String feedbackParam = ""; try { @@ -3864,6 +3894,7 @@ public class PreferencesDialog extends javax.swing.JDialog { private javax.swing.JCheckBox cbEnableGameSounds; private javax.swing.JCheckBox cbEnableOtherSounds; private javax.swing.JCheckBox cbEnableSkipButtonsSounds; + private javax.swing.JCheckBox cbGameJsonLogAutoSave; private javax.swing.JCheckBox cbGameLogAutoSave; private javax.swing.JComboBox cbNumberOfDownloadThreads; private javax.swing.JCheckBox cbPassPriorityActivation; diff --git a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java index 386c2156a69..8dc603a1f2d 100644 --- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -47,6 +47,8 @@ import mage.client.util.audio.AudioManager; import mage.client.util.object.SaveObjectUtil; import mage.interfaces.callback.CallbackClient; import mage.interfaces.callback.ClientCallback; +import mage.remote.ActionData; +import mage.remote.Session; import mage.utils.CompressUtil; import mage.view.*; import mage.view.ChatMessage.MessageType; @@ -102,7 +104,6 @@ public class CallbackClientImpl implements CallbackClient { break; case CHATMESSAGE: { ChatMessage message = (ChatMessage) callback.getData(); - // Drop messages from ignored users if (message.getUsername() != null && IgnoreList.IGNORED_MESSAGE_TYPES.contains(message.getMessageType())) { final String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> ""); @@ -183,6 +184,7 @@ public class CallbackClientImpl implements CallbackClient { case GAME_INIT: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_INIT", callback.getObjectId(), (GameView) callback.getData()); panel.init((GameView) callback.getData()); } break; @@ -190,6 +192,13 @@ public class CallbackClientImpl implements CallbackClient { case GAME_OVER: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + Session session = SessionHandler.getSession(); + if (session.isJsonLogActive()) { + appendJsonEvent("GAME_OVER", callback.getObjectId(), callback.getData()); + ActionData actionData = appendJsonEvent("GAME_OVER", callback.getObjectId(), callback.getData()); + String logFileName = "game-" + actionData.gameId + ".json"; + S3Uploader.upload(logFileName, actionData.gameId.toString()); + } panel.endMessage((String) callback.getData(), callback.getMessageId()); } break; @@ -201,6 +210,7 @@ public class CallbackClientImpl implements CallbackClient { GameClientMessage message = (GameClientMessage) callback.getData(); GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_ASK", callback.getObjectId(), message); panel.ask(message.getMessage(), message.getGameView(), callback.getMessageId(), message.getOptions()); } break; @@ -208,8 +218,10 @@ public class CallbackClientImpl implements CallbackClient { case GAME_TARGET: // e.g. Pick triggered ability { GameClientMessage message = (GameClientMessage) callback.getData(); + GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_TARGET", callback.getObjectId(), message); panel.pickTarget(message.getMessage(), message.getCardsView(), message.getGameView(), message.getTargets(), message.isFlag(), message.getOptions(), callback.getMessageId()); } @@ -217,8 +229,10 @@ public class CallbackClientImpl implements CallbackClient { } case GAME_SELECT: { GameClientMessage message = (GameClientMessage) callback.getData(); + GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_SELECT", callback.getObjectId(), message); panel.select(message.getMessage(), message.getGameView(), callback.getMessageId(), message.getOptions()); } break; @@ -226,6 +240,7 @@ public class CallbackClientImpl implements CallbackClient { case GAME_CHOOSE_ABILITY: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_CHOOSE_PILE", callback.getObjectId(), callback.getData()); panel.pickAbility((AbilityPickerView) callback.getData()); } break; @@ -234,15 +249,18 @@ public class CallbackClientImpl implements CallbackClient { GameClientMessage message = (GameClientMessage) callback.getData(); GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_CHOOSE_PILE", callback.getObjectId(), message); panel.pickPile(message.getMessage(), message.getPile1(), message.getPile2()); } break; } case GAME_CHOOSE_CHOICE: { GameClientMessage message = (GameClientMessage) callback.getData(); + GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_CHOOSE_CHOICE", callback.getObjectId(), message); panel.getChoice(message.getChoice(), callback.getObjectId()); } break; @@ -251,35 +269,45 @@ public class CallbackClientImpl implements CallbackClient { GameClientMessage message = (GameClientMessage) callback.getData(); GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_PLAY_MANA", callback.getObjectId(), message); panel.playMana(message.getMessage(), message.getGameView(), message.getOptions(), callback.getMessageId()); } break; } case GAME_PLAY_XMANA: { GameClientMessage message = (GameClientMessage) callback.getData(); + GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_PLAY_XMANA", callback.getObjectId(), message); panel.playXMana(message.getMessage(), message.getGameView(), callback.getMessageId()); } break; } case GAME_GET_AMOUNT: { GameClientMessage message = (GameClientMessage) callback.getData(); + GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_GET_AMOUNT", callback.getObjectId(), message); + panel.getAmount(message.getMin(), message.getMax(), message.getMessage()); } break; } case GAME_UPDATE: { GamePanel panel = MageFrame.getGame(callback.getObjectId()); + if (panel != null) { + appendJsonEvent("GAME_UPDATE", callback.getObjectId(), callback.getData()); + panel.updateGame((GameView) callback.getData()); } break; } case END_GAME_INFO: MageFrame.getInstance().showGameEndDialog((GameEndView) callback.getData()); + break; case SHOW_USERMESSAGE: List messageData = (List) callback.getData(); @@ -293,6 +321,7 @@ public class CallbackClientImpl implements CallbackClient { GameClientMessage message = (GameClientMessage) callback.getData(); GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { + appendJsonEvent("GAME_INFORM", callback.getObjectId(), message); panel.inform(message.getMessage(), message.getGameView(), callback.getMessageId()); } } @@ -376,6 +405,17 @@ public class CallbackClientImpl implements CallbackClient { }); } + private ActionData appendJsonEvent(String name, UUID gameId, Object value) { + Session session = SessionHandler.getSession(); + if (session.isJsonLogActive()) { + ActionData actionData = new ActionData(name, gameId); + actionData.value = value; + session.appendJsonLog(actionData); + return actionData; + } + return null; + } + private void createChatStartMessage(ChatPanelBasic chatPanel) { chatPanel.setStartMessageDone(true); ChatPanelBasic usedPanel = chatPanel; diff --git a/Mage.Client/src/main/java/mage/client/remote/S3Uploader.java b/Mage.Client/src/main/java/mage/client/remote/S3Uploader.java new file mode 100644 index 00000000000..35a1b538ce1 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/remote/S3Uploader.java @@ -0,0 +1,45 @@ +package mage.client.remote; + +import com.amazonaws.AmazonClientException; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.transfer.TransferManager; +import com.amazonaws.services.s3.transfer.Upload; +import java.io.File; +import org.apache.log4j.Logger; + +public class S3Uploader { + + private static final Logger logger = Logger.getLogger(S3Uploader.class); + + public static Boolean upload(String filePath, String keyName) throws Exception { + String existingBucketName = System.getenv("S3_BUCKET") != null ? System.getenv("S3_BUCKET") + : "xmage-game-logs-dev"; + + String accessKeyId = System.getenv("AWS_ACCESS_ID"); + String secretKeyId = System.getenv("AWS_SECRET_KEY"); + + if (accessKeyId == null || "".equals(accessKeyId) + || secretKeyId == null || "".equals(secretKeyId) + || existingBucketName == null || "".equals(existingBucketName)) { + logger.info("Aborting json log sync."); + return false; + } + + String path = new File("./" + filePath).getCanonicalPath(); + logger.info("Syncing " + path + " to bucket: " + existingBucketName + " with AWS Access Id: " + accessKeyId); + + BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKeyId, secretKeyId); + TransferManager tm = new TransferManager(awsCreds); + Upload upload = tm.upload(existingBucketName, "/game/" + keyName + ".json", new File(path)); + + try { + upload.waitForUploadResult(); + logger.info("Sync Complete For " + path + " to bucket: " + existingBucketName + " with AWS Access Id: " + accessKeyId); + new File(path); + return true; + } catch (AmazonClientException amazonClientException) { + logger.fatal("Unable to upload file, upload was aborted.", amazonClientException); + return 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 547282f16dc..5c11b3c6dc9 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -1444,11 +1444,14 @@ class TableTableModel extends AbstractTableModel { if (tables[arg0].isTournament()) { return "Show"; } else { - owner = tables[arg0].getControllerName(); + owner = tables[arg0].getControllerName(); if (SessionHandler.getSession() != null && owner.equals(SessionHandler.getUserName())) { return ""; } - return "Watch"; + if (tables[arg0].getSpectatorsAllowed()) { + return "Watch"; + } + return ""; } default: return ""; diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java index a6903e42a1f..8b3c5d9589b 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java @@ -1,6 +1,14 @@ package org.mage.card.arcane; -import java.awt.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.awt.image.FilteredImageSource; import java.awt.image.ImageProducer; @@ -18,13 +26,18 @@ import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; import java.util.regex.Pattern; import javax.imageio.ImageIO; import javax.swing.*; - import mage.cards.repository.ExpansionRepository; +import mage.client.constants.Constants; +import mage.client.constants.Constants.ResourceSetSize; +import mage.client.constants.Constants.ResourceSymbolSize; import mage.client.util.GUISizeHelper; import mage.client.util.ImageHelper; import mage.client.util.gui.BufferedImageBuilder; @@ -36,13 +49,6 @@ import org.apache.batik.transcoder.TranscodingHints; import org.apache.batik.transcoder.image.ImageTranscoder; import org.apache.batik.util.SVGConstants; import org.apache.log4j.Logger; - -import mage.client.constants.Constants; -import mage.client.constants.Constants.ResourceSymbolSize; -import mage.client.constants.Constants.ResourceSetSize; - -import org.jdesktop.swingx.graphics.ShadowRenderer; -import org.jdesktop.swingx.graphics.GraphicsUtilities; import org.mage.plugins.card.utils.CardImageUtils; public final class ManaSymbols { @@ -71,9 +77,14 @@ public final class ManaSymbols { private static final Map setImagesExist = new HashMap<>(); private static final Pattern REPLACE_SYMBOLS_PATTERN = Pattern.compile("\\{([^}/]*)/?([^}]*)\\}"); private static String cachedPath; - private static final String[] symbols = new String[]{"0", "1", "10", "11", "12", "15", "16", "2", "3", "4", "5", "6", "7", "8", "9", "B", "BG", - "BR", "G", "GU", "GW", "R", "RG", "RW", "S", "T", "U", "UB", "UR", "W", "WB", "WU", - "WP", "UP", "BP", "RP", "GP", "X", "C", "E"}; + private static final String[] symbols = new String[]{"0", "1", "10", "11", "12", "15", "16", "2", "3", "4", "5", "6", "7", "8", "9", + "B", "BG", "BR", "BP", "2B", + "G", "GU", "GW", "GP", "2G", + "R", "RG", "RW", "RP", "2R", + "S", "T", + "U", "UB", "UR", "UP", "2U", + "W", "WB", "WU", "WP", "2W", + "X", "C", "E"}; private static final JLabel labelRender = new JLabel(); // render mana text @@ -93,22 +104,21 @@ public final class ManaSymbols { // save symbol images in png for html replacement in texts // you can add bigger size for better quality Map pngImages = manaImages.get(50); - if (pngImages != null){ + if (pngImages != null) { File pngPath = new File(getResourceSymbolsPath(ResourceSymbolSize.PNG)); if (!pngPath.exists()) { pngPath.mkdirs(); } - for(String symbol: symbols){ - try - { + for (String symbol : symbols) { + try { BufferedImage image = pngImages.get(symbol); - if (image != null){ + if (image != null) { File newFile = new File(pngPath.getPath() + File.separator + symbol + ".png"); ImageIO.write(image, "png", newFile); } - }catch (Exception e) { + } catch (Exception e) { LOGGER.warn("Can't generate png image for symbol:" + symbol); } } @@ -222,8 +232,7 @@ public final class ManaSymbols { // load SVG image // base loader code: https://stackoverflow.com/questions/11435671/how-to-get-a-buffererimage-from-a-svg // resize code: https://vibranttechie.wordpress.com/2015/05/15/svg-loading-to-javafx-stage-and-auto-scaling-when-stage-resize/ - - if (useShadow && ((resizeToWidth <= 0) || (resizeToHeight <= 0))){ + if (useShadow && ((resizeToWidth <= 0) || (resizeToHeight <= 0))) { throw new IllegalArgumentException("Must use non zero sizes for shadow."); } @@ -234,12 +243,12 @@ public final class ManaSymbols { // These defaults emphasize quality and precision, and // are more similar to the defaults of other SVG viewers. // SVG documents can still override these defaults. - String css = "svg {" + - "shape-rendering: geometricPrecision;" + - "text-rendering: geometricPrecision;" + - "color-rendering: optimizeQuality;" + - "image-rendering: optimizeQuality;" + - "}"; + String css = "svg {" + + "shape-rendering: geometricPrecision;" + + "text-rendering: geometricPrecision;" + + "color-rendering: optimizeQuality;" + + "image-rendering: optimizeQuality;" + + "}"; File cssFile = File.createTempFile("batik-default-override-", ".css"); FileWriter w = new FileWriter(cssFile); w.write(css); @@ -250,7 +259,7 @@ public final class ManaSymbols { // resize int shadowX = 0; int shadowY = 0; - if(useShadow) { + if (useShadow) { // shadow size (16px image: 1px left, 2px bottom) shadowX = 1 * Math.round(1f / 16f * resizeToWidth); shadowY = 2 * Math.round(1f / 16f * resizeToHeight); @@ -258,11 +267,11 @@ public final class ManaSymbols { resizeToHeight = resizeToHeight - shadowY; }; - if(resizeToWidth > 0){ - transcoderHints.put(ImageTranscoder.KEY_WIDTH, (float)resizeToWidth); //your image width + if (resizeToWidth > 0) { + transcoderHints.put(ImageTranscoder.KEY_WIDTH, (float) resizeToWidth); //your image width } - if(resizeToHeight > 0){ - transcoderHints.put(ImageTranscoder.KEY_HEIGHT, (float)resizeToHeight); //your image height + if (resizeToHeight > 0) { + transcoderHints.put(ImageTranscoder.KEY_HEIGHT, (float) resizeToHeight); //your image height } transcoderHints.put(ImageTranscoder.KEY_XML_PARSER_VALIDATING, Boolean.FALSE); @@ -293,14 +302,13 @@ public final class ManaSymbols { t.transcode(input, null); } catch (Exception e) { throw new IOException("Couldn't convert svg file: " + svgFile + " , reason: " + e.getMessage()); - } - finally { + } finally { cssFile.delete(); } BufferedImage originImage = imagePointer[0]; - if(useShadow && (originImage.getWidth() > 0)){ + if (useShadow && (originImage.getWidth() > 0)) { // draw shadow // origin image was reduces in sizes to fit shadow // see https://stackoverflow.com/a/40833715/1276632 @@ -309,10 +317,11 @@ public final class ManaSymbols { ImageProducer prod = new FilteredImageSource(originImage.getSource(), new RGBImageFilter() { @Override public int filterRGB(int x, int y, int rgb) { - if (rgb == 0) + if (rgb == 0) { return 0; - else + } else { return 0xff000000; + } } }); // create whe black image @@ -325,7 +334,7 @@ public final class ManaSymbols { // draw original image g.drawImage(originImage, 0, 0, null); return result; - }else{ + } else { // return origin image without shadow return originImage; } @@ -340,23 +349,22 @@ public final class ManaSymbols { ShadowRenderer renderer = new ShadowRenderer(shadowSize, 0.5f, Color.GRAY); return renderer.createShadow(base); - */ - + */ //imagePointer[0]; } - public static File getSymbolFileNameAsSVG(String symbol){ + public static File getSymbolFileNameAsSVG(String symbol) { return new File(getResourceSymbolsPath(ResourceSymbolSize.SVG) + symbol + ".svg"); } - private static BufferedImage loadSymbolAsSVG(String symbol, int resizeToWidth, int resizeToHeight){ + private static BufferedImage loadSymbolAsSVG(String symbol, int resizeToWidth, int resizeToHeight) { File sourceFile = getSymbolFileNameAsSVG(symbol); return loadSymbolAsSVG(sourceFile, resizeToWidth, resizeToHeight); } - private static BufferedImage loadSymbolAsSVG(File sourceFile, int resizeToWidth, int resizeToHeight){ - try{ + private static BufferedImage loadSymbolAsSVG(File sourceFile, int resizeToWidth, int resizeToHeight) { + try { // no need to resize svg (lib already do it on load) return loadSVG(sourceFile, resizeToWidth, resizeToHeight, true); @@ -366,12 +374,12 @@ public final class ManaSymbols { } } - private static File getSymbolFileNameAsGIF(String symbol, int size){ + private static File getSymbolFileNameAsGIF(String symbol, int size) { ResourceSymbolSize needSize = null; - if (size <= 15){ + if (size <= 15) { needSize = ResourceSymbolSize.SMALL; - }else if (size <= 25){ + } else if (size <= 25) { needSize = ResourceSymbolSize.MEDIUM; } else { needSize = ResourceSymbolSize.LARGE; @@ -380,20 +388,20 @@ public final class ManaSymbols { return new File(getResourceSymbolsPath(needSize) + symbol + ".gif"); } - private static BufferedImage loadSymbolAsGIF(String symbol, int resizeToWidth, int resizeToHeight){ + private static BufferedImage loadSymbolAsGIF(String symbol, int resizeToWidth, int resizeToHeight) { File file = getSymbolFileNameAsGIF(symbol, resizeToWidth); return loadSymbolAsGIF(file, resizeToWidth, resizeToHeight); } - private static BufferedImage loadSymbolAsGIF(File sourceFile, int resizeToWidth, int resizeToHeight){ + private static BufferedImage loadSymbolAsGIF(File sourceFile, int resizeToWidth, int resizeToHeight) { BufferedImage image = null; try { - if ((resizeToWidth == 15) || (resizeToWidth == 25)){ + if ((resizeToWidth == 15) || (resizeToWidth == 25)) { // normal size image = ImageIO.read(sourceFile); - }else{ + } else { // resize size image = ImageIO.read(sourceFile); @@ -407,7 +415,7 @@ public final class ManaSymbols { return null; } - return image; + return image; } private static boolean loadSymbolImages(int size) { @@ -454,7 +462,7 @@ public final class ManaSymbols { private static void renameSymbols(String path) { File file = new File(path); - if (!file.exists()){ + if (!file.exists()) { return; } @@ -475,7 +483,7 @@ public final class ManaSymbols { } } - private static String getResourceSymbolsPath(ResourceSymbolSize needSize){ + private static String getResourceSymbolsPath(ResourceSymbolSize needSize) { // return real path to symbols (default or user defined) String path = CardImageUtils.getImagesDir() + Constants.RESOURCE_PATH_SYMBOLS + File.separator; @@ -503,15 +511,14 @@ public final class ManaSymbols { } // fix double separator if size folder is not set - while(path.endsWith(File.separator)) - { + while (path.endsWith(File.separator)) { path = path.substring(0, path.length() - 1); } return path + File.separator; } - private static String getResourceSetsPath(ResourceSetSize needSize){ + private static String getResourceSetsPath(ResourceSetSize needSize) { // return real path to sets icons (default or user defined) String path = CardImageUtils.getImagesDir() + Constants.RESOURCE_PATH_SYMBOLS + File.separator; @@ -533,8 +540,7 @@ public final class ManaSymbols { } // fix double separator if size folder is not set - while(path.endsWith(File.separator)) - { + while (path.endsWith(File.separator)) { path = path.substring(0, path.length() - 1); } @@ -592,10 +598,7 @@ public final class ManaSymbols { Graphics2D gg = image.createGraphics(); manaPanel.paint(gg); g.drawImage(image, x, y, null); -*/ - - - + */ // OLD version with custom draw Map sizedSymbols = manaImages.get(symbolWidth); if (manaCost.isEmpty()) { @@ -627,8 +630,8 @@ public final class ManaSymbols { int stringWidth = labelRender.getFontMetrics(labelFont).stringWidth(labelText); int componentWidth = labelRender.getWidth(); // Find out how much the font can grow in width. - double widthRatio = (double)componentWidth / (double)stringWidth; - int newFontSize = (int)(labelFont.getSize() * widthRatio); + double widthRatio = (double) componentWidth / (double) stringWidth; + int newFontSize = (int) (labelFont.getSize() * widthRatio); int componentHeight = labelRender.getHeight(); // Pick a new font size so it will not be larger than the height of label. int fontSizeToUse = Math.min(newFontSize, componentHeight); @@ -638,11 +641,11 @@ public final class ManaSymbols { // render component to new position // need to copy graphics, overvise it draw at top left corner // https://stackoverflow.com/questions/4974268/java-paint-problem - Graphics2D labelG = (Graphics2D)g.create(x, y, symbolWidth, symbolWidth); + Graphics2D labelG = (Graphics2D) g.create(x, y, symbolWidth, symbolWidth); labelG.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); labelG.fillOval(x + 1, y + 1, symbolWidth - 2, symbolWidth - 2); labelRender.paint(labelG); - }else { + } else { // ICON draw g.drawImage(image, x, y, null); } @@ -666,12 +669,12 @@ public final class ManaSymbols { TOOLTIP, } - private static String filePathToUrl(String path){ + private static String filePathToUrl(String path) { // convert file path to uri path (for html docs) - if((path != null) && (!path.equals(""))){ + if ((path != null) && (!path.equals(""))) { File file = new File(path); return file.toURI().toString(); - }else{ + } else { return null; } } @@ -680,7 +683,6 @@ public final class ManaSymbols { // mana cost to HTML images (urls to files) // do not use it for new code - try to suppotr svg render - int symbolSize; switch (type) { case TABLE: @@ -702,22 +704,20 @@ public final class ManaSymbols { // auto size ResourceSymbolSize needSize = null; - if (symbolSize <= 15){ + if (symbolSize <= 15) { needSize = ResourceSymbolSize.SMALL; - }else if (symbolSize <= 25){ + } else if (symbolSize <= 25) { needSize = ResourceSymbolSize.MEDIUM; } else { needSize = ResourceSymbolSize.LARGE; } // replace every {symbol} to link - // ignore data backup String replaced = value .replace("{source}", "|source|") .replace("{this}", "|this|"); - // not need to add different images (width and height do the work) // use best png size (generated on startup) TODO: add reload images after update String htmlImagesPath = getResourceSymbolsPath(ResourceSymbolSize.PNG); @@ -725,8 +725,8 @@ public final class ManaSymbols { .replace("$", "@S@"); // paths with $ will rise error, need escape that replaced = REPLACE_SYMBOLS_PATTERN.matcher(replaced).replaceAll( - "$1$2 { break; default: - // Nothing to do, symbol is available in the large size + // Nothing to do, symbol is available in the large size } } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java index f13d7fbf1c4..db32e1f1519 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java @@ -268,7 +268,7 @@ public enum WizardCardsImageSource implements CardImageSource { supportedSets.add("V17"); // From the Vault: Transform supportedSets.add("UST"); // Unstable supportedSets.add("RIX"); // Rivals of Ixalan -// supportedSets.add("A25"); // Masters 25 + supportedSets.add("A25"); // Masters 25 // supportedSets.add("DOM"); // Dominaria // supportedSets.add("M19"); // Core 2019 diff --git a/Mage.Common/pom.xml b/Mage.Common/pom.xml index 27914c196db..1ec1165dec4 100644 --- a/Mage.Common/pom.xml +++ b/Mage.Common/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.27 + 1.4.28 mage-common @@ -25,6 +25,7 @@ jspf-core 0.9.1
+ org.jboss.remoting jboss-remoting @@ -50,7 +51,11 @@ trove 1.0.2 - + + com.google.code.gson + gson + 2.8.2 +
diff --git a/Mage.Common/src/main/java/mage/remote/ActionData.java b/Mage.Common/src/main/java/mage/remote/ActionData.java new file mode 100644 index 00000000000..51d079a0acf --- /dev/null +++ b/Mage.Common/src/main/java/mage/remote/ActionData.java @@ -0,0 +1,138 @@ +/* + * Copyright 2018 nanarpuss_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 com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.Expose; +import java.util.UUID; + +public class ActionData { + + @Expose + public UUID gameId; + @Expose + public String sessionId; + @Expose + public String type; + @Expose + public Object value; + @Expose + public String message; + + public String toJson() { + GsonBuilder gsonBuilder = new GsonBuilder(); + Gson gson = gsonBuilder.setExclusionStrategies(new CustomExclusionStrategy()).create(); + + return gson.toJson(this); + } + + public ActionData(String type, UUID gameId, String sessionId) { + this.type = type; + this.sessionId = sessionId; + this.gameId = gameId; + } + + public ActionData(String type, UUID gameId) { + this.type = type; + this.gameId = gameId; + } + + public class CustomExclusionStrategy implements ExclusionStrategy { + + // FIXME: Very crude way of whitelisting, as it applies to all levels of the JSON tree. + private final java.util.Set KEEP = new java.util.HashSet<>( + java.util.Arrays.asList( + new String[]{ + "id", + "choice", + "damage", + "abilityType", + "ability", + "abilities", + "method", + "data", + "options", + "life", + "players", + "zone", + "step", + "phase", + "attackers", + "blockers", + "tapped", + "damage", + "combat", + "paid", + "hand", + "stack", + "convertedManaCost", + "gameId", + "canPlayInHand", + "gameView", + "sessionId", + "power", + "choices", + "targets", + "loyalty", + "toughness", + "power", + "type", + "priorityTime", + "manaCost", + "value", + "message", + "cardsView", + "name", + "count", + "counters", + "battlefield", + "parentId" + })); + + public CustomExclusionStrategy() { + } + + // This method is called for all fields. if the method returns true the + // field is excluded from serialization + @Override + public boolean shouldSkipField(FieldAttributes f) { + String name = f.getName(); + return !KEEP.contains(name); + } + + // This method is called for all classes. If the method returns true the + // class is excluded. + @Override + public boolean shouldSkipClass(Class clazz) { + return false; + } + } +} diff --git a/Mage.Common/src/main/java/mage/remote/Session.java b/Mage.Common/src/main/java/mage/remote/Session.java index 8cb062c63ef..e7f04049ca0 100644 --- a/Mage.Common/src/main/java/mage/remote/Session.java +++ b/Mage.Common/src/main/java/mage/remote/Session.java @@ -24,8 +24,7 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ - + */ package mage.remote; import mage.remote.interfaces.ChatSession; @@ -46,4 +45,5 @@ import mage.remote.interfaces.Testable; */ public interface Session extends ClientData, Connect, GamePlay, GameTypes, ServerState, ChatSession, Feedback, PlayerActions, Replays, Testable { + public void appendJsonLog(ActionData actionData); } diff --git a/Mage.Common/src/main/java/mage/remote/SessionImpl.java b/Mage.Common/src/main/java/mage/remote/SessionImpl.java index 1f7bca2c0cb..6d766b7398c 100644 --- a/Mage.Common/src/main/java/mage/remote/SessionImpl.java +++ b/Mage.Common/src/main/java/mage/remote/SessionImpl.java @@ -27,7 +27,11 @@ */ package mage.remote; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; import java.io.IOException; +import java.io.PrintWriter; import java.lang.reflect.UndeclaredThrowableException; import java.net.*; import java.util.*; @@ -86,6 +90,7 @@ public class SessionImpl implements Session { private static boolean debugMode = false; private boolean canceled = false; + private boolean jsonLogActive = false; static { debugMode = System.getProperty("debug.mage") != null; @@ -798,6 +803,9 @@ public class SessionImpl implements Session { public boolean sendPlayerUUID(UUID gameId, UUID data) { try { if (isConnected()) { + ActionData actionData = new ActionData("SEND_PLAYER_UUID", gameId, getSessionId()); + actionData.value = data; + appendJsonLog(actionData); server.sendPlayerUUID(gameId, sessionId, data); return true; } @@ -813,6 +821,10 @@ public class SessionImpl implements Session { public boolean sendPlayerBoolean(UUID gameId, boolean data) { try { if (isConnected()) { + ActionData actionData = new ActionData("SEND_PLAYER_BOOLEAN", gameId, getSessionId()); + actionData.value = data; + appendJsonLog(actionData); + server.sendPlayerBoolean(gameId, sessionId, data); return true; } @@ -828,6 +840,10 @@ public class SessionImpl implements Session { public boolean sendPlayerInteger(UUID gameId, int data) { try { if (isConnected()) { + ActionData actionData = new ActionData("SEND_PLAYER_INTEGER", gameId, getSessionId()); + actionData.value = data; + appendJsonLog(actionData); + server.sendPlayerInteger(gameId, sessionId, data); return true; } @@ -843,6 +859,10 @@ public class SessionImpl implements Session { public boolean sendPlayerString(UUID gameId, String data) { try { if (isConnected()) { + ActionData actionData = new ActionData("SEND_PLAYER_STRING", gameId, getSessionId()); + actionData.value = data; + appendJsonLog(actionData); + server.sendPlayerString(gameId, sessionId, data); return true; } @@ -858,6 +878,9 @@ public class SessionImpl implements Session { public boolean sendPlayerManaType(UUID gameId, UUID playerId, ManaType data) { try { if (isConnected()) { + ActionData actionData = new ActionData("SEND_PLAYER_MANA_TYPE", gameId, getSessionId()); + actionData.value = data; + appendJsonLog(actionData); server.sendPlayerManaType(gameId, playerId, sessionId, data); return true; } @@ -869,6 +892,25 @@ public class SessionImpl implements Session { return false; } + @Override + public void appendJsonLog(ActionData actionData) { + if (isJsonLogActive()) { + String dir = "gamelogsJson"; + File saveDir = new File(dir); + //Existence check + if (!saveDir.exists()) { + saveDir.mkdirs(); + } + actionData.sessionId = getSessionId(); + String logFileName = dir + File.separator + "game-" + actionData.gameId + ".json"; + try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(logFileName, true)))) { + out.println(actionData.toJson()); + } catch (IOException e) { + logger.error("Cant write JSON game log file - " + logFileName, e); + } + } + } + @Override public DraftPickView sendCardPick(UUID draftId, UUID cardId, Set hiddenCards) { try { @@ -1274,6 +1316,11 @@ public class SessionImpl implements Session { public boolean sendPlayerAction(PlayerAction passPriorityAction, UUID gameId, Object data) { try { if (isConnected()) { + ActionData actionData = new ActionData("SEND_PLAYER_ACTION", gameId, getSessionId()); + + actionData.value = passPriorityAction + (data != null ? " " + data.toString() : ""); + appendJsonLog(actionData); + server.sendPlayerAction(passPriorityAction, gameId, sessionId, data); return true; } @@ -1597,6 +1644,16 @@ public class SessionImpl implements Session { } } + @Override + public boolean isJsonLogActive() { + return jsonLogActive; + } + + @Override + public void setJsonLogActive(boolean jsonLogActive) { + this.jsonLogActive = jsonLogActive; + } + } class MageAuthenticator extends Authenticator { diff --git a/Mage.Common/src/main/java/mage/remote/interfaces/ClientData.java b/Mage.Common/src/main/java/mage/remote/interfaces/ClientData.java index 741b69b1b54..b8b29df27df 100644 --- a/Mage.Common/src/main/java/mage/remote/interfaces/ClientData.java +++ b/Mage.Common/src/main/java/mage/remote/interfaces/ClientData.java @@ -37,4 +37,8 @@ public interface ClientData { String getUserName(); boolean updatePreferencesForServer(UserData userData); + + void setJsonLogActive(boolean active); + + boolean isJsonLogActive(); } diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java index 2e87becec46..90979d8b476 100644 --- a/Mage.Common/src/main/java/mage/utils/MageVersion.java +++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java @@ -40,8 +40,8 @@ public class MageVersion implements Serializable, Comparable { */ public final static int MAGE_VERSION_MAJOR = 1; public final static int MAGE_VERSION_MINOR = 4; - public final static int MAGE_VERSION_PATCH = 27; - public final static String MAGE_VERSION_MINOR_PATCH = "V4"; + public final static int MAGE_VERSION_PATCH = 28; + public final static String MAGE_VERSION_MINOR_PATCH = "V0"; public final static String MAGE_VERSION_INFO = ""; private final int major; diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index d9154f7418c..b2ed194eb8a 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -53,6 +53,8 @@ import mage.target.Target; import mage.target.Targets; import mage.util.SubTypeList; +import com.google.gson.annotations.Expose; + /** * @author BetaSteward_at_googlemail.com */ @@ -61,11 +63,17 @@ public class CardView extends SimpleCardView { private static final long serialVersionUID = 1L; protected UUID parentId; + @Expose protected String name; + @Expose protected String displayName; + @Expose protected List rules; + @Expose protected String power; + @Expose protected String toughness; + @Expose protected String loyalty = ""; protected String startingLoyalty; protected EnumSet cardTypes; @@ -110,7 +118,6 @@ public class CardView extends SimpleCardView { protected ArtRect artRect = ArtRect.NORMAL; protected List targets; - protected UUID pairedCard; protected List bandedCards; protected boolean paid; diff --git a/Mage.Common/src/main/java/mage/view/GameClientMessage.java b/Mage.Common/src/main/java/mage/view/GameClientMessage.java index 33af5e25e9a..1774c766f9e 100644 --- a/Mage.Common/src/main/java/mage/view/GameClientMessage.java +++ b/Mage.Common/src/main/java/mage/view/GameClientMessage.java @@ -32,6 +32,11 @@ import java.io.Serializable; import java.util.Map; import java.util.Set; import java.util.UUID; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.Expose; + import mage.choices.Choice; /** @@ -39,18 +44,29 @@ import mage.choices.Choice; * @author BetaSteward_at_googlemail.com */ public class GameClientMessage implements Serializable { + @Expose private static final long serialVersionUID = 1L; - + @Expose private GameView gameView; + @Expose private CardsView cardsView; + @Expose private CardsView cardsView2; + @Expose private String message; + @Expose private boolean flag; + @Expose private String[] strings; + @Expose private Set targets; + @Expose private int min; + @Expose private int max; + @Expose private Map options; + @Expose private Choice choice; public GameClientMessage(GameView gameView) { @@ -155,4 +171,11 @@ public class GameClientMessage implements Serializable { return choice; } + public String toJson() { + Gson gson = new GsonBuilder() + .excludeFieldsWithoutExposeAnnotation() + .create(); + return gson.toJson(this); + } + } diff --git a/Mage.Common/src/main/java/mage/view/GameView.java b/Mage.Common/src/main/java/mage/view/GameView.java index 2e16415ad69..b354503c567 100644 --- a/Mage.Common/src/main/java/mage/view/GameView.java +++ b/Mage.Common/src/main/java/mage/view/GameView.java @@ -28,11 +28,17 @@ package mage.view; import java.io.Serializable; +import java.io.BufferedWriter; +import java.io.PrintWriter; +import java.io.FileWriter; +import java.io.IOException; + import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; + import mage.MageObject; import mage.abilities.costs.Cost; import mage.cards.Card; @@ -54,6 +60,8 @@ import mage.game.stack.StackObject; import mage.players.Player; import mage.watchers.common.CastSpellLastTurnWatcher; import org.apache.log4j.Logger; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; /** * @@ -64,7 +72,6 @@ public class GameView implements Serializable { private static final long serialVersionUID = 1L; private static final Logger LOGGER = Logger.getLogger(GameView.class); - private final int priorityTime; private final List players = new ArrayList<>(); private CardsView hand; @@ -351,4 +358,8 @@ public class GameView implements Serializable { return rollbackTurnsAllowed; } + public String toJson() { + Gson gson = new GsonBuilder().create(); + return gson.toJson(this); + } } diff --git a/Mage.Common/src/main/java/mage/view/SimpleCardView.java b/Mage.Common/src/main/java/mage/view/SimpleCardView.java index 709e45ad837..a137f0ca0e9 100644 --- a/Mage.Common/src/main/java/mage/view/SimpleCardView.java +++ b/Mage.Common/src/main/java/mage/view/SimpleCardView.java @@ -28,6 +28,8 @@ package mage.view; +import com.google.gson.annotations.Expose; + import java.io.Serializable; import java.util.UUID; @@ -36,6 +38,7 @@ import java.util.UUID; * @author BetaSteward_at_googlemail.com */ public class SimpleCardView implements Serializable { + @Expose protected UUID id; protected String expansionSetCode; protected String tokenSetCode; diff --git a/Mage.Common/src/main/java/mage/view/TableView.java b/Mage.Common/src/main/java/mage/view/TableView.java index dd7de79d347..3480328b615 100644 --- a/Mage.Common/src/main/java/mage/view/TableView.java +++ b/Mage.Common/src/main/java/mage/view/TableView.java @@ -65,6 +65,7 @@ public class TableView implements Serializable { private final boolean limited; private final boolean rated; private final boolean passworded; + private final boolean spectatorsAllowed; public TableView(Table table) { this.tableId = table.getId(); @@ -139,6 +140,7 @@ public class TableView implements Serializable { this.limited = table.getMatch().getOptions().isLimited(); this.rated = table.getMatch().getOptions().isRated(); this.passworded = !table.getMatch().getOptions().getPassword().isEmpty(); + this.spectatorsAllowed = table.getMatch().getOptions().isSpectatorsAllowed(); } else { // TOURNAMENT if (table.getTournament().getOptions().getNumberRounds() > 0) { @@ -186,6 +188,7 @@ public class TableView implements Serializable { this.limited = table.getTournament().getOptions().getMatchOptions().isLimited(); this.rated = table.getTournament().getOptions().getMatchOptions().isRated(); this.passworded = !table.getTournament().getOptions().getPassword().isEmpty(); + this.spectatorsAllowed = table.getTournament().getOptions().isWatchingAllowed(); } } @@ -200,6 +203,11 @@ public class TableView implements Serializable { public String getControllerName() { return controllerName; } + + public boolean getSpectatorsAllowed() { + return spectatorsAllowed; + } + public String getGameType() { return gameType; diff --git a/Mage.Plugins/Mage.Counter.Plugin/pom.xml b/Mage.Plugins/Mage.Counter.Plugin/pom.xml index fb18ef46031..d2944cb269c 100644 --- a/Mage.Plugins/Mage.Counter.Plugin/pom.xml +++ b/Mage.Plugins/Mage.Counter.Plugin/pom.xml @@ -7,7 +7,7 @@ org.mage mage-plugins - 1.4.27 + 1.4.28 mage-counter-plugin diff --git a/Mage.Plugins/pom.xml b/Mage.Plugins/pom.xml index 1f416e020a2..7e6f273822e 100644 --- a/Mage.Plugins/pom.xml +++ b/Mage.Plugins/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.27 + 1.4.28 mage-plugins diff --git a/Mage.Server.Console/pom.xml b/Mage.Server.Console/pom.xml index da623f3a2e7..9e108f53171 100644 --- a/Mage.Server.Console/pom.xml +++ b/Mage.Server.Console/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.27 + 1.4.28 org.mage diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml b/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml index 265f1916b8e..ab625726adb 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-deck-constructed diff --git a/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml b/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml index 2c62fe02811..aa81d459f7d 100644 --- a/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml +++ b/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-deck-limited diff --git a/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml index 3527ff58a12..fcefb189c85 100644 --- a/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-game-canadianhighlanderduel diff --git a/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml index bb230f2daa5..92b105dbabd 100644 --- a/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-game-commanderduel diff --git a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml index f117ef478eb..79236c53bd0 100644 --- a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml @@ -6,7 +6,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-game-commanderfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml index ec70531d05f..35a8cce789c 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-game-freeforall diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml index 3a81b5a6d1b..ba54fbebeb6 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml @@ -6,7 +6,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-game-freeformcommanderfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml index e7b84af8696..fe2bbdc083d 100644 --- a/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-game-momirduel diff --git a/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml b/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml index 45aec3e9feb..2efafc2d568 100644 --- a/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-game-momirfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml index 6d129bbccfc..a55cb05de16 100644 --- a/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml @@ -6,7 +6,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-game-pennydreadfulcommanderfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml index 8956c20ce35..b8db38b14a6 100644 --- a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-game-tinyleadersduel diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml index f1258b6370c..701cd2d51a0 100644 --- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-game-twoplayerduel diff --git a/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml b/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml index 918591d77ab..b73916d86f1 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-player-ai-draftbot diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml b/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml index 8e6f78c71ab..55430e49844 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-player-ai-ma 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 e313d6b7633..287b31752c6 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 @@ -522,7 +522,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { continue; } if (!sim.checkIfGameIsOver() - && action.isUsesStack()) { + && (action.isUsesStack() || action instanceof PassAbility)) { // only pass if the last action uses the stack UUID nextPlayerId = sim.getPlayerList().get(); do { @@ -533,8 +533,8 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { SimulationNode2 newNode = new SimulationNode2(node, sim, action, depth, currentPlayer.getId()); sim.checkStateAndTriggered(); int val; - if (action instanceof PassAbility) { - // Stop to simulate deeper if PassAbility + if (action instanceof PassAbility && sim.getStack().isEmpty()) { + // Stop to simulate deeper if PassAbility and stack is empty val = GameStateEvaluator2.evaluate(this.getId(), sim); } else { val = addActions(newNode, depth - 1, alpha, beta); 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 71c7da0196f..baadef53665 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 @@ -109,6 +109,12 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } return false; case DECLARE_BLOCKERS: + printOutState(game); + if (actions.isEmpty()) { + calculateActions(game); + } + act(game); + return true; case FIRST_COMBAT_DAMAGE: case COMBAT_DAMAGE: case END_COMBAT: diff --git a/Mage.Server.Plugins/Mage.Player.AI/pom.xml b/Mage.Server.Plugins/Mage.Player.AI/pom.xml index 035ce5ac692..c25e70ebbea 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AI/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-player-ai 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 472036cc249..cbe5cdd49ed 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 @@ -1312,6 +1312,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (numAvailable < 0) { numAvailable = 0; } + if (numAvailable > max) { + numAvailable = max; + } return numAvailable; } diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml b/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml index 55ac6fe3f6d..9aaeefe83e6 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-player-ai-mcts diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml b/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml index ca65afe7bab..5116ebee21a 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-player-aiminimax diff --git a/Mage.Server.Plugins/Mage.Player.Human/pom.xml b/Mage.Server.Plugins/Mage.Player.Human/pom.xml index bc027a7b019..341f00780be 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.Human/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-player-human 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 349d39a8594..3f3a20f0d8c 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 @@ -1111,11 +1111,13 @@ public class HumanPlayer extends PlayerImpl { if (object == null) { return; } - Spell spell = game.getStack().getSpell(abilityToCast.getSourceId()); - if (spell != null && !spell.isResolving() - && spell.isDoneActivatingManaAbilities()) { - game.informPlayer(this, "You can no longer use activated mana abilities to pay for the current spell. Cancel and recast the spell and activate mana abilities first."); - return; + if (AbilityType.SPELL.equals(abilityToCast.getAbilityType())) { + Spell spell = game.getStack().getSpell(abilityToCast.getSourceId()); + if (spell != null && !spell.isResolving() + && spell.isDoneActivatingManaAbilities()) { + game.informPlayer(this, "You can no longer use activated mana abilities to pay for the current spell. Cancel and recast the spell and activate mana abilities first."); + return; + } } Zone zone = game.getState().getZone(object.getId()); if (zone != null) { diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml index 545883a229a..84ebd49f9fc 100644 --- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-tournament-boosterdraft diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/LegacyCube2018February.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/LegacyCube2018February.java new file mode 100644 index 00000000000..da3002e8629 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/LegacyCube2018February.java @@ -0,0 +1,641 @@ +/* + * 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.tournament.cubes; + +import mage.game.draft.DraftCube; + +/** + * + * @author LevelX2 + */ +public class LegacyCube2018February extends DraftCube { + + public LegacyCube2018February() { + super("MTGO Legacy Cube February 2018 (600 cards)"); + cubeCards.add(new DraftCube.CardIdentity("Abbot of Keral Keep", "")); + cubeCards.add(new DraftCube.CardIdentity("Abrade", "")); + cubeCards.add(new DraftCube.CardIdentity("Abrupt Decay", "")); + cubeCards.add(new DraftCube.CardIdentity("Abyssal Persecutor", "")); + cubeCards.add(new DraftCube.CardIdentity("Acidic Slime", "")); + cubeCards.add(new DraftCube.CardIdentity("Act of Aggression", "")); + cubeCards.add(new DraftCube.CardIdentity("Adanto Vanguard", "")); + cubeCards.add(new DraftCube.CardIdentity("Ajani Goldmane", "")); + cubeCards.add(new DraftCube.CardIdentity("Ajani Vengeant", "")); + cubeCards.add(new DraftCube.CardIdentity("Anafenza, Kin-Tree Spirit", "")); + cubeCards.add(new DraftCube.CardIdentity("Ancestral Vision", "")); + cubeCards.add(new DraftCube.CardIdentity("Ancient Tomb", "")); + cubeCards.add(new DraftCube.CardIdentity("Angel of Invention", "")); + cubeCards.add(new DraftCube.CardIdentity("Angel of Serenity", "")); + cubeCards.add(new DraftCube.CardIdentity("Anger of the Gods", "")); + cubeCards.add(new DraftCube.CardIdentity("Anguished Unmaking", "")); + cubeCards.add(new DraftCube.CardIdentity("Animate Dead", "")); + cubeCards.add(new DraftCube.CardIdentity("Approach of the Second Sun", "")); + cubeCards.add(new DraftCube.CardIdentity("Arbor Elf", "")); + cubeCards.add(new DraftCube.CardIdentity("Arc Trail", "")); + cubeCards.add(new DraftCube.CardIdentity("Archangel Avacyn", "")); + cubeCards.add(new DraftCube.CardIdentity("Arid Mesa", "")); + cubeCards.add(new DraftCube.CardIdentity("Armageddon", "")); + cubeCards.add(new DraftCube.CardIdentity("Ashiok, Nightmare Weaver", "")); + cubeCards.add(new DraftCube.CardIdentity("Assemble the Legion", "")); + cubeCards.add(new DraftCube.CardIdentity("Augur of Bolas", "")); + cubeCards.add(new DraftCube.CardIdentity("Avacyn's Pilgrim", "")); + cubeCards.add(new DraftCube.CardIdentity("Avalanche Riders", "")); + cubeCards.add(new DraftCube.CardIdentity("Avenger of Zendikar", "")); + cubeCards.add(new DraftCube.CardIdentity("Badlands", "")); + cubeCards.add(new DraftCube.CardIdentity("Baleful Strix", "")); + cubeCards.add(new DraftCube.CardIdentity("Banefire", "")); + cubeCards.add(new DraftCube.CardIdentity("Baneslayer Angel", "")); + cubeCards.add(new DraftCube.CardIdentity("Banisher Priest", "")); + cubeCards.add(new DraftCube.CardIdentity("Banishing Light", "")); + cubeCards.add(new DraftCube.CardIdentity("Baral, Chief of Compliance", "")); + cubeCards.add(new DraftCube.CardIdentity("Batterskull", "")); + cubeCards.add(new DraftCube.CardIdentity("Bayou", "")); + cubeCards.add(new DraftCube.CardIdentity("Beast Within", "")); + cubeCards.add(new DraftCube.CardIdentity("Bedlam Reveler", "")); + cubeCards.add(new DraftCube.CardIdentity("Birds of Paradise", "")); + cubeCards.add(new DraftCube.CardIdentity("Birthing Pod", "")); + cubeCards.add(new DraftCube.CardIdentity("Bitterblossom", "")); + cubeCards.add(new DraftCube.CardIdentity("Blackcleave Cliffs", "")); + cubeCards.add(new DraftCube.CardIdentity("Blade Splicer", "")); + cubeCards.add(new DraftCube.CardIdentity("Blood Crypt", "")); + cubeCards.add(new DraftCube.CardIdentity("Bloodbraid Elf", "")); + cubeCards.add(new DraftCube.CardIdentity("Bloodghast", "")); + cubeCards.add(new DraftCube.CardIdentity("Bloodstained Mire", "")); + cubeCards.add(new DraftCube.CardIdentity("Bloom Tender", "")); + cubeCards.add(new DraftCube.CardIdentity("Blooming Marsh", "")); + cubeCards.add(new DraftCube.CardIdentity("Bogardan Hellkite", "")); + cubeCards.add(new DraftCube.CardIdentity("Bomat Courier", "")); + cubeCards.add(new DraftCube.CardIdentity("Bone Shredder", "")); + cubeCards.add(new DraftCube.CardIdentity("Bonfire of the Damned", "")); + cubeCards.add(new DraftCube.CardIdentity("Boros Charm", "")); + cubeCards.add(new DraftCube.CardIdentity("Boros Reckoner", "")); + cubeCards.add(new DraftCube.CardIdentity("Botanical Sanctum", "")); + cubeCards.add(new DraftCube.CardIdentity("Brainstorm", "")); + cubeCards.add(new DraftCube.CardIdentity("Breeding Pool", "")); + cubeCards.add(new DraftCube.CardIdentity("Brimaz, King of Oreskos", "")); + cubeCards.add(new DraftCube.CardIdentity("Brimstone Volley", "")); + cubeCards.add(new DraftCube.CardIdentity("Bruna, the Fading Light", "")); + cubeCards.add(new DraftCube.CardIdentity("Buried Alive", "")); + cubeCards.add(new DraftCube.CardIdentity("Burst Lightning", "")); + cubeCards.add(new DraftCube.CardIdentity("Careful Consideration", "")); + cubeCards.add(new DraftCube.CardIdentity("Careful Study", "")); + cubeCards.add(new DraftCube.CardIdentity("Carnage Tyrant", "")); + cubeCards.add(new DraftCube.CardIdentity("Cathartic Reunion", "")); + cubeCards.add(new DraftCube.CardIdentity("Celestial Colonnade", "")); + cubeCards.add(new DraftCube.CardIdentity("Censor", "")); + cubeCards.add(new DraftCube.CardIdentity("Chain Lightning", "")); + cubeCards.add(new DraftCube.CardIdentity("Chainer's Edict", "")); + cubeCards.add(new DraftCube.CardIdentity("Chandra's Phoenix", "")); + cubeCards.add(new DraftCube.CardIdentity("Chandra, Fire of Kaladesh", "")); + cubeCards.add(new DraftCube.CardIdentity("Chandra, Flamecaller", "")); + cubeCards.add(new DraftCube.CardIdentity("Chandra, Pyromaster", "")); + cubeCards.add(new DraftCube.CardIdentity("Chandra, Torch of Defiance", "")); + cubeCards.add(new DraftCube.CardIdentity("Char", "")); + cubeCards.add(new DraftCube.CardIdentity("Chart a Course", "")); + cubeCards.add(new DraftCube.CardIdentity("Chord of Calling", "")); + cubeCards.add(new DraftCube.CardIdentity("Chromatic Lantern", "")); + cubeCards.add(new DraftCube.CardIdentity("City of Brass", "")); + cubeCards.add(new DraftCube.CardIdentity("Clifftop Retreat", "")); + cubeCards.add(new DraftCube.CardIdentity("Cloudgoat Ranger", "")); + cubeCards.add(new DraftCube.CardIdentity("Coalition Relic", "")); + cubeCards.add(new DraftCube.CardIdentity("Coldsteel Heart", "")); + cubeCards.add(new DraftCube.CardIdentity("Collective Brutality", "")); + cubeCards.add(new DraftCube.CardIdentity("Collective Defiance", "")); + cubeCards.add(new DraftCube.CardIdentity("Collective Effort", "")); + cubeCards.add(new DraftCube.CardIdentity("Compulsive Research", "")); + cubeCards.add(new DraftCube.CardIdentity("Concealed Courtyard", "")); + cubeCards.add(new DraftCube.CardIdentity("Condemn", "")); + cubeCards.add(new DraftCube.CardIdentity("Consecrated Sphinx", "")); + cubeCards.add(new DraftCube.CardIdentity("Control Magic", "")); + cubeCards.add(new DraftCube.CardIdentity("Copperline Gorge", "")); + cubeCards.add(new DraftCube.CardIdentity("Council's Judgment", "")); + cubeCards.add(new DraftCube.CardIdentity("Counterspell", "")); + cubeCards.add(new DraftCube.CardIdentity("Courser of Kruphix", "")); + cubeCards.add(new DraftCube.CardIdentity("Crater's Claws", "")); + cubeCards.add(new DraftCube.CardIdentity("Craterhoof Behemoth", "")); + cubeCards.add(new DraftCube.CardIdentity("Creeping Tar Pit", "")); + cubeCards.add(new DraftCube.CardIdentity("Crux of Fate", "")); + cubeCards.add(new DraftCube.CardIdentity("Cryptic Command", "")); + cubeCards.add(new DraftCube.CardIdentity("Cultivate", "")); + cubeCards.add(new DraftCube.CardIdentity("Cyclonic Rift", "")); + cubeCards.add(new DraftCube.CardIdentity("Damnation", "")); + cubeCards.add(new DraftCube.CardIdentity("Dark Confidant", "")); + cubeCards.add(new DraftCube.CardIdentity("Dark Petition", "")); + cubeCards.add(new DraftCube.CardIdentity("Dark Ritual", "")); + cubeCards.add(new DraftCube.CardIdentity("Darkslick Shores", "")); + cubeCards.add(new DraftCube.CardIdentity("Day of Judgment", "")); + cubeCards.add(new DraftCube.CardIdentity("Daze", "")); + cubeCards.add(new DraftCube.CardIdentity("Deathrite Shaman", "")); + cubeCards.add(new DraftCube.CardIdentity("Deceiver Exarch", "")); + cubeCards.add(new DraftCube.CardIdentity("Declaration in Stone", "")); + cubeCards.add(new DraftCube.CardIdentity("Delver of Secrets", "")); + cubeCards.add(new DraftCube.CardIdentity("Den Protector", "")); + cubeCards.add(new DraftCube.CardIdentity("Deranged Hermit", "")); + cubeCards.add(new DraftCube.CardIdentity("Desecration Demon", "")); + cubeCards.add(new DraftCube.CardIdentity("Devil's Play", "")); + cubeCards.add(new DraftCube.CardIdentity("Devoted Druid", "")); + cubeCards.add(new DraftCube.CardIdentity("Disallow", "")); + cubeCards.add(new DraftCube.CardIdentity("Disfigure", "")); + cubeCards.add(new DraftCube.CardIdentity("Dismember", "")); + cubeCards.add(new DraftCube.CardIdentity("Dismiss", "")); + cubeCards.add(new DraftCube.CardIdentity("Dissipate", "")); + cubeCards.add(new DraftCube.CardIdentity("Dissolve", "")); + cubeCards.add(new DraftCube.CardIdentity("Distended Mindbender", "")); + cubeCards.add(new DraftCube.CardIdentity("Domri Rade", "")); + cubeCards.add(new DraftCube.CardIdentity("Doom Blade", "")); + cubeCards.add(new DraftCube.CardIdentity("Dragonlord Atarka", "")); + cubeCards.add(new DraftCube.CardIdentity("Dragonlord Dromoka", "")); + cubeCards.add(new DraftCube.CardIdentity("Dragonlord Ojutai", "")); + cubeCards.add(new DraftCube.CardIdentity("Dragonlord Silumgar", "")); + cubeCards.add(new DraftCube.CardIdentity("Dragonskull Summit", "")); + cubeCards.add(new DraftCube.CardIdentity("Drana, Liberator of Malakir", "")); + cubeCards.add(new DraftCube.CardIdentity("Dread Return", "")); + cubeCards.add(new DraftCube.CardIdentity("Dreadbore", "")); + cubeCards.add(new DraftCube.CardIdentity("Dromoka's Command", "")); + cubeCards.add(new DraftCube.CardIdentity("Drowned Catacomb", "")); + cubeCards.add(new DraftCube.CardIdentity("Dualcaster Mage", "")); + cubeCards.add(new DraftCube.CardIdentity("Dungeon Geists", "")); + cubeCards.add(new DraftCube.CardIdentity("Duplicant", "")); + cubeCards.add(new DraftCube.CardIdentity("Duress", "")); + cubeCards.add(new DraftCube.CardIdentity("Duskwatch Recruiter", "")); + cubeCards.add(new DraftCube.CardIdentity("Edric, Spymaster of Trest", "")); + cubeCards.add(new DraftCube.CardIdentity("Elder Deep-Fiend", "")); + cubeCards.add(new DraftCube.CardIdentity("Electrolyze", "")); + cubeCards.add(new DraftCube.CardIdentity("Elesh Norn, Grand Cenobite", "")); + cubeCards.add(new DraftCube.CardIdentity("Elspeth Tirel", "")); + cubeCards.add(new DraftCube.CardIdentity("Elspeth, Knight-Errant", "")); + cubeCards.add(new DraftCube.CardIdentity("Elspeth, Sun's Champion", "")); + cubeCards.add(new DraftCube.CardIdentity("Elves of Deep Shadow", "")); + cubeCards.add(new DraftCube.CardIdentity("Elvish Mystic", "")); + cubeCards.add(new DraftCube.CardIdentity("Emeria Angel", "")); + cubeCards.add(new DraftCube.CardIdentity("Emrakul, the Aeons Torn", "")); + cubeCards.add(new DraftCube.CardIdentity("Emrakul, the Promised End", "")); + cubeCards.add(new DraftCube.CardIdentity("Entomb", "")); + cubeCards.add(new DraftCube.CardIdentity("Entreat the Angels", "")); + cubeCards.add(new DraftCube.CardIdentity("Erebos, God of the Dead", "")); + cubeCards.add(new DraftCube.CardIdentity("Eternal Witness", "")); + cubeCards.add(new DraftCube.CardIdentity("Exhume", "")); + cubeCards.add(new DraftCube.CardIdentity("Explore", "")); + cubeCards.add(new DraftCube.CardIdentity("Exquisite Firecraft", "")); + cubeCards.add(new DraftCube.CardIdentity("Fact or Fiction", "")); + cubeCards.add(new DraftCube.CardIdentity("Faith's Fetters", "")); + cubeCards.add(new DraftCube.CardIdentity("Falkenrath Gorger", "")); + cubeCards.add(new DraftCube.CardIdentity("Farseek", "")); + cubeCards.add(new DraftCube.CardIdentity("Fatal Push", "")); + cubeCards.add(new DraftCube.CardIdentity("Fauna Shaman", "")); + cubeCards.add(new DraftCube.CardIdentity("Fertile Ground", "")); + cubeCards.add(new DraftCube.CardIdentity("Field of Ruin", "")); + cubeCards.add(new DraftCube.CardIdentity("Fiend Hunter", "")); + cubeCards.add(new DraftCube.CardIdentity("Fiery Confluence", "")); + cubeCards.add(new DraftCube.CardIdentity("Fire // Ice", "")); + cubeCards.add(new DraftCube.CardIdentity("Firebolt", "")); + cubeCards.add(new DraftCube.CardIdentity("Flame Slash", "")); + cubeCards.add(new DraftCube.CardIdentity("Flametongue Kavu", "")); + cubeCards.add(new DraftCube.CardIdentity("Flickerwisp", "")); + cubeCards.add(new DraftCube.CardIdentity("Flooded Strand", "")); + cubeCards.add(new DraftCube.CardIdentity("Forbid", "")); + cubeCards.add(new DraftCube.CardIdentity("Force of Will", "")); + cubeCards.add(new DraftCube.CardIdentity("Force Spike", "")); + cubeCards.add(new DraftCube.CardIdentity("Forked Bolt", "")); + cubeCards.add(new DraftCube.CardIdentity("Frost Titan", "")); + cubeCards.add(new DraftCube.CardIdentity("Fyndhorn Elves", "")); + cubeCards.add(new DraftCube.CardIdentity("Gaea's Cradle", "")); + cubeCards.add(new DraftCube.CardIdentity("Garruk Relentless", "")); + cubeCards.add(new DraftCube.CardIdentity("Garruk Wildspeaker", "")); + cubeCards.add(new DraftCube.CardIdentity("Garruk, Apex Predator", "")); + cubeCards.add(new DraftCube.CardIdentity("Garruk, Primal Hunter", "")); + cubeCards.add(new DraftCube.CardIdentity("Gatekeeper of Malakir", "")); + cubeCards.add(new DraftCube.CardIdentity("Geist of Saint Traft", "")); + cubeCards.add(new DraftCube.CardIdentity("Genesis Wave", "")); + cubeCards.add(new DraftCube.CardIdentity("Geralf's Messenger", "")); + cubeCards.add(new DraftCube.CardIdentity("Ghor-Clan Rampager", "")); + cubeCards.add(new DraftCube.CardIdentity("Gideon Jura", "")); + cubeCards.add(new DraftCube.CardIdentity("Gideon of the Trials", "")); + cubeCards.add(new DraftCube.CardIdentity("Gideon, Ally of Zendikar", "")); + cubeCards.add(new DraftCube.CardIdentity("Gifted Aetherborn", "")); + cubeCards.add(new DraftCube.CardIdentity("Gifts Ungiven", "")); + cubeCards.add(new DraftCube.CardIdentity("Gilded Lotus", "")); + cubeCards.add(new DraftCube.CardIdentity("Gisela, the Broken Blade", "")); + cubeCards.add(new DraftCube.CardIdentity("Gitaxian Probe", "")); + cubeCards.add(new DraftCube.CardIdentity("Glacial Fortress", "")); + cubeCards.add(new DraftCube.CardIdentity("Glen Elendra Archmage", "")); + cubeCards.add(new DraftCube.CardIdentity("Glorious Anthem", "")); + cubeCards.add(new DraftCube.CardIdentity("Glorybringer", "")); + cubeCards.add(new DraftCube.CardIdentity("Go for the Throat", "")); + cubeCards.add(new DraftCube.CardIdentity("Goblin Dark-Dwellers", "")); + cubeCards.add(new DraftCube.CardIdentity("Goblin Guide", "")); + cubeCards.add(new DraftCube.CardIdentity("Goblin Rabblemaster", "")); + cubeCards.add(new DraftCube.CardIdentity("God-Pharaoh's Gift", "")); + cubeCards.add(new DraftCube.CardIdentity("Godless Shrine", "")); + cubeCards.add(new DraftCube.CardIdentity("Gonti, Lord of Luxury", "")); + cubeCards.add(new DraftCube.CardIdentity("Grave Titan", "")); + cubeCards.add(new DraftCube.CardIdentity("Gray Merchant of Asphodel", "")); + cubeCards.add(new DraftCube.CardIdentity("Greater Gargadon", "")); + cubeCards.add(new DraftCube.CardIdentity("Green Sun's Zenith", "")); + cubeCards.add(new DraftCube.CardIdentity("Greenwarden of Murasa", "")); + cubeCards.add(new DraftCube.CardIdentity("Grim Lavamancer", "")); + cubeCards.add(new DraftCube.CardIdentity("Griselbrand", "")); + cubeCards.add(new DraftCube.CardIdentity("Hallowed Fountain", "")); + cubeCards.add(new DraftCube.CardIdentity("Hallowed Spiritkeeper", "")); + cubeCards.add(new DraftCube.CardIdentity("Hangarback Walker", "")); + cubeCards.add(new DraftCube.CardIdentity("Harbinger of the Tides", "")); + cubeCards.add(new DraftCube.CardIdentity("Harmonize", "")); + cubeCards.add(new DraftCube.CardIdentity("Hazoret the Fervent", "")); + cubeCards.add(new DraftCube.CardIdentity("Hellrider", "")); + cubeCards.add(new DraftCube.CardIdentity("Hero of Bladehold", "")); + cubeCards.add(new DraftCube.CardIdentity("Hero's Downfall", "")); + cubeCards.add(new DraftCube.CardIdentity("Hidden Dragonslayer", "")); + cubeCards.add(new DraftCube.CardIdentity("Hinterland Harbor", "")); + cubeCards.add(new DraftCube.CardIdentity("Hissing Quagmire", "")); + cubeCards.add(new DraftCube.CardIdentity("Honor of the Pure", "")); + cubeCards.add(new DraftCube.CardIdentity("Hordeling Outburst", "")); + cubeCards.add(new DraftCube.CardIdentity("Hornet Queen", "")); + cubeCards.add(new DraftCube.CardIdentity("Hostage Taker", "")); + cubeCards.add(new DraftCube.CardIdentity("Hour of Devastation", "")); + cubeCards.add(new DraftCube.CardIdentity("Huntmaster of the Fells", "")); + cubeCards.add(new DraftCube.CardIdentity("Hymn to Tourach", "")); + cubeCards.add(new DraftCube.CardIdentity("Hypnotic Specter", "")); + cubeCards.add(new DraftCube.CardIdentity("Icefall Regent", "")); + cubeCards.add(new DraftCube.CardIdentity("Imperial Recruiter", "")); + cubeCards.add(new DraftCube.CardIdentity("Impulse", "")); + cubeCards.add(new DraftCube.CardIdentity("Incendiary Flow", "")); + cubeCards.add(new DraftCube.CardIdentity("Incinerate", "")); + cubeCards.add(new DraftCube.CardIdentity("Inferno Titan", "")); + cubeCards.add(new DraftCube.CardIdentity("Inquisition of Kozilek", "")); + cubeCards.add(new DraftCube.CardIdentity("Inspiring Vantage", "")); + cubeCards.add(new DraftCube.CardIdentity("Into the Roil", "")); + cubeCards.add(new DraftCube.CardIdentity("Ire Shaman", "")); + cubeCards.add(new DraftCube.CardIdentity("Isamaru, Hound of Konda", "")); + cubeCards.add(new DraftCube.CardIdentity("Isolated Chapel", "")); + cubeCards.add(new DraftCube.CardIdentity("Izzet Charm", "")); + cubeCards.add(new DraftCube.CardIdentity("Jace Beleren", "")); + cubeCards.add(new DraftCube.CardIdentity("Jace, Architect of Thought", "")); + cubeCards.add(new DraftCube.CardIdentity("Jace, the Mind Sculptor", "")); + cubeCards.add(new DraftCube.CardIdentity("Jace, Vryn's Prodigy", "")); + cubeCards.add(new DraftCube.CardIdentity("Jackal Pup", "")); + cubeCards.add(new DraftCube.CardIdentity("Jadelight Ranger", "")); + cubeCards.add(new DraftCube.CardIdentity("Joraga Treespeaker", "")); + cubeCards.add(new DraftCube.CardIdentity("Journey to Nowhere", "")); + cubeCards.add(new DraftCube.CardIdentity("Kalitas, Traitor of Ghet", "")); + cubeCards.add(new DraftCube.CardIdentity("Karmic Guide", "")); + cubeCards.add(new DraftCube.CardIdentity("Karn Liberated", "")); + cubeCards.add(new DraftCube.CardIdentity("Keiga, the Tide Star", "")); + cubeCards.add(new DraftCube.CardIdentity("Keranos, God of Storms", "")); + cubeCards.add(new DraftCube.CardIdentity("Kiki-Jiki, Mirror Breaker", "")); + cubeCards.add(new DraftCube.CardIdentity("Kiln Fiend", "")); + cubeCards.add(new DraftCube.CardIdentity("Kiora's Follower", "")); + cubeCards.add(new DraftCube.CardIdentity("Kiora, the Crashing Wave", "")); + cubeCards.add(new DraftCube.CardIdentity("Kira, Great Glass-Spinner", "")); + cubeCards.add(new DraftCube.CardIdentity("Kitchen Finks", "")); + cubeCards.add(new DraftCube.CardIdentity("Kitesail Freebooter", "")); + cubeCards.add(new DraftCube.CardIdentity("Knight of the Reliquary", "")); + cubeCards.add(new DraftCube.CardIdentity("Kodama's Reach", "")); + cubeCards.add(new DraftCube.CardIdentity("Kokusho, the Evening Star", "")); + cubeCards.add(new DraftCube.CardIdentity("Kolaghan's Command", "")); + cubeCards.add(new DraftCube.CardIdentity("Koth of the Hammer", "")); + cubeCards.add(new DraftCube.CardIdentity("Kytheon, Hero of Akros", "")); + cubeCards.add(new DraftCube.CardIdentity("Land Tax", "")); + cubeCards.add(new DraftCube.CardIdentity("Languish", "")); + cubeCards.add(new DraftCube.CardIdentity("Lavaclaw Reaches", "")); + cubeCards.add(new DraftCube.CardIdentity("Legacy's Allure", "")); + cubeCards.add(new DraftCube.CardIdentity("Legion's Landing", "")); + cubeCards.add(new DraftCube.CardIdentity("Leonin Relic-Warder", "")); + cubeCards.add(new DraftCube.CardIdentity("Lightning Bolt", "")); + cubeCards.add(new DraftCube.CardIdentity("Lightning Greaves", "")); + cubeCards.add(new DraftCube.CardIdentity("Lightning Helix", "")); + cubeCards.add(new DraftCube.CardIdentity("Lightning Mauler", "")); + cubeCards.add(new DraftCube.CardIdentity("Lightning Strike", "")); + cubeCards.add(new DraftCube.CardIdentity("Liliana of the Veil", "")); + cubeCards.add(new DraftCube.CardIdentity("Liliana Vess", "")); + cubeCards.add(new DraftCube.CardIdentity("Liliana, Death's Majesty", "")); + cubeCards.add(new DraftCube.CardIdentity("Liliana, Heretical Healer", "")); + cubeCards.add(new DraftCube.CardIdentity("Liliana, the Last Hope", "")); + cubeCards.add(new DraftCube.CardIdentity("Lingering Souls", "")); + cubeCards.add(new DraftCube.CardIdentity("Linvala, Keeper of Silence", "")); + cubeCards.add(new DraftCube.CardIdentity("Linvala, the Preserver", "")); + cubeCards.add(new DraftCube.CardIdentity("Living Death", "")); + cubeCards.add(new DraftCube.CardIdentity("Llanowar Elves", "")); + cubeCards.add(new DraftCube.CardIdentity("Looter il-Kor", "")); + cubeCards.add(new DraftCube.CardIdentity("Lotus Cobra", "")); + cubeCards.add(new DraftCube.CardIdentity("Loxodon Warhammer", "")); + cubeCards.add(new DraftCube.CardIdentity("Lumbering Falls", "")); + cubeCards.add(new DraftCube.CardIdentity("Maelstrom Pulse", "")); + cubeCards.add(new DraftCube.CardIdentity("Magma Jet", "")); + cubeCards.add(new DraftCube.CardIdentity("Makeshift Mannequin", "")); + cubeCards.add(new DraftCube.CardIdentity("Malicious Affliction", "")); + cubeCards.add(new DraftCube.CardIdentity("Man-o'-War", "")); + cubeCards.add(new DraftCube.CardIdentity("Mana Confluence", "")); + cubeCards.add(new DraftCube.CardIdentity("Mana Tithe", "")); + cubeCards.add(new DraftCube.CardIdentity("Managorger Hydra", "")); + cubeCards.add(new DraftCube.CardIdentity("Manic Vandal", "")); + cubeCards.add(new DraftCube.CardIdentity("Marsh Flats", "")); + cubeCards.add(new DraftCube.CardIdentity("Martial Coup", "")); + cubeCards.add(new DraftCube.CardIdentity("Massacre Wurm", "")); + cubeCards.add(new DraftCube.CardIdentity("Master of the Wild Hunt", "")); + cubeCards.add(new DraftCube.CardIdentity("Master of Waves", "")); + cubeCards.add(new DraftCube.CardIdentity("Maze of Ith", "")); + cubeCards.add(new DraftCube.CardIdentity("Mentor of the Meek", "")); + cubeCards.add(new DraftCube.CardIdentity("Merfolk Looter", "")); + cubeCards.add(new DraftCube.CardIdentity("Mimic Vat", "")); + cubeCards.add(new DraftCube.CardIdentity("Mind Stone", "")); + cubeCards.add(new DraftCube.CardIdentity("Mirari's Wake", "")); + cubeCards.add(new DraftCube.CardIdentity("Mirran Crusader", "")); + cubeCards.add(new DraftCube.CardIdentity("Mirror Entity", "")); + cubeCards.add(new DraftCube.CardIdentity("Miscalculation", "")); + cubeCards.add(new DraftCube.CardIdentity("Mishra's Factory", "")); + cubeCards.add(new DraftCube.CardIdentity("Misty Rainforest", "")); + cubeCards.add(new DraftCube.CardIdentity("Mizzium Mortars", "")); + cubeCards.add(new DraftCube.CardIdentity("Mogg War Marshal", "")); + cubeCards.add(new DraftCube.CardIdentity("Monastery Mentor", "")); + cubeCards.add(new DraftCube.CardIdentity("Monastery Swiftspear", "")); + cubeCards.add(new DraftCube.CardIdentity("Mother of Runes", "")); + cubeCards.add(new DraftCube.CardIdentity("Mulldrifter", "")); + cubeCards.add(new DraftCube.CardIdentity("Murderous Cut", "")); + cubeCards.add(new DraftCube.CardIdentity("Murderous Redcap", "")); + cubeCards.add(new DraftCube.CardIdentity("Mutavault", "")); + cubeCards.add(new DraftCube.CardIdentity("Myr Battlesphere", "")); + cubeCards.add(new DraftCube.CardIdentity("Mystic Snake", "")); + cubeCards.add(new DraftCube.CardIdentity("Nahiri, the Harbinger", "")); + cubeCards.add(new DraftCube.CardIdentity("Natural Order", "")); + cubeCards.add(new DraftCube.CardIdentity("Nature's Lore", "")); + cubeCards.add(new DraftCube.CardIdentity("Necromancy", "")); + cubeCards.add(new DraftCube.CardIdentity("Needle Spires", "")); + cubeCards.add(new DraftCube.CardIdentity("Negate", "")); + cubeCards.add(new DraftCube.CardIdentity("Nekrataal", "")); + cubeCards.add(new DraftCube.CardIdentity("Nevinyrral's Disk", "")); + cubeCards.add(new DraftCube.CardIdentity("Nicol Bolas, God-Pharaoh", "")); + cubeCards.add(new DraftCube.CardIdentity("Nicol Bolas, Planeswalker", "")); + cubeCards.add(new DraftCube.CardIdentity("Nightveil Specter", "")); + cubeCards.add(new DraftCube.CardIdentity("Nimble Obstructionist", "")); + cubeCards.add(new DraftCube.CardIdentity("Nissa, Vastwood Seer", "")); + cubeCards.add(new DraftCube.CardIdentity("Nissa, Vital Force", "")); + cubeCards.add(new DraftCube.CardIdentity("Nissa, Voice of Zendikar", "")); + cubeCards.add(new DraftCube.CardIdentity("Nissa, Worldwaker", "")); + cubeCards.add(new DraftCube.CardIdentity("Noble Hierarch", "")); + cubeCards.add(new DraftCube.CardIdentity("Noxious Gearhulk", "")); + cubeCards.add(new DraftCube.CardIdentity("Nykthos, Shrine to Nyx", "")); + cubeCards.add(new DraftCube.CardIdentity("Oath of Nissa", "")); + cubeCards.add(new DraftCube.CardIdentity("Ob Nixilis Reignited", "")); + cubeCards.add(new DraftCube.CardIdentity("Oblivion Ring", "")); + cubeCards.add(new DraftCube.CardIdentity("Oblivion Stone", "")); + cubeCards.add(new DraftCube.CardIdentity("Obstinate Baloth", "")); + cubeCards.add(new DraftCube.CardIdentity("Old Man of the Sea", "")); + cubeCards.add(new DraftCube.CardIdentity("Olivia Voldaren", "")); + cubeCards.add(new DraftCube.CardIdentity("Oona's Prowler", "")); + cubeCards.add(new DraftCube.CardIdentity("Ophiomancer", "")); + cubeCards.add(new DraftCube.CardIdentity("Opposition", "")); + cubeCards.add(new DraftCube.CardIdentity("Opt", "")); + cubeCards.add(new DraftCube.CardIdentity("Oracle of Mul Daya", "")); + cubeCards.add(new DraftCube.CardIdentity("Oust", "")); + cubeCards.add(new DraftCube.CardIdentity("Outpost Siege", "")); + cubeCards.add(new DraftCube.CardIdentity("Overgrown Battlement", "")); + cubeCards.add(new DraftCube.CardIdentity("Overgrown Tomb", "")); + cubeCards.add(new DraftCube.CardIdentity("Pack Rat", "")); + cubeCards.add(new DraftCube.CardIdentity("Pact of Negation", "")); + cubeCards.add(new DraftCube.CardIdentity("Parallax Wave", "")); + cubeCards.add(new DraftCube.CardIdentity("Part the Waterveil", "")); + cubeCards.add(new DraftCube.CardIdentity("Path to Exile", "")); + cubeCards.add(new DraftCube.CardIdentity("Pernicious Deed", "")); + cubeCards.add(new DraftCube.CardIdentity("Pestermite", "")); + cubeCards.add(new DraftCube.CardIdentity("Phantasmal Image", "")); + cubeCards.add(new DraftCube.CardIdentity("Phyrexian Arena", "")); + cubeCards.add(new DraftCube.CardIdentity("Phyrexian Metamorph", "")); + cubeCards.add(new DraftCube.CardIdentity("Phyrexian Obliterator", "")); + cubeCards.add(new DraftCube.CardIdentity("Phyrexian Rager", "")); + cubeCards.add(new DraftCube.CardIdentity("Phyrexian Revoker", "")); + cubeCards.add(new DraftCube.CardIdentity("Pia and Kiran Nalaar", "")); + cubeCards.add(new DraftCube.CardIdentity("Pillar of Flame", "")); + cubeCards.add(new DraftCube.CardIdentity("Plateau", "")); + cubeCards.add(new DraftCube.CardIdentity("Polluted Delta", "")); + cubeCards.add(new DraftCube.CardIdentity("Polukranos, World Eater", "")); + cubeCards.add(new DraftCube.CardIdentity("Ponder", "")); + cubeCards.add(new DraftCube.CardIdentity("Porcelain Legionnaire", "")); + cubeCards.add(new DraftCube.CardIdentity("Precinct Captain", "")); + cubeCards.add(new DraftCube.CardIdentity("Preordain", "")); + cubeCards.add(new DraftCube.CardIdentity("Primal Command", "")); + cubeCards.add(new DraftCube.CardIdentity("Primeval Titan", "")); + cubeCards.add(new DraftCube.CardIdentity("Profane Command", "")); + cubeCards.add(new DraftCube.CardIdentity("Purphoros, God of the Forge", "")); + cubeCards.add(new DraftCube.CardIdentity("Pyroclasm", "")); + cubeCards.add(new DraftCube.CardIdentity("Qasali Pridemage", "")); + cubeCards.add(new DraftCube.CardIdentity("Quarantine Field", "")); + cubeCards.add(new DraftCube.CardIdentity("Raging Ravine", "")); + cubeCards.add(new DraftCube.CardIdentity("Raise the Alarm", "")); + cubeCards.add(new DraftCube.CardIdentity("Rakdos's Return", "")); + cubeCards.add(new DraftCube.CardIdentity("Ral Zarek", "")); + cubeCards.add(new DraftCube.CardIdentity("Rampaging Baloths", "")); + cubeCards.add(new DraftCube.CardIdentity("Rampaging Ferocidon", "")); + cubeCards.add(new DraftCube.CardIdentity("Rampant Growth", "")); + cubeCards.add(new DraftCube.CardIdentity("Ravages of War", "")); + cubeCards.add(new DraftCube.CardIdentity("Ravenous Chupacabra", "")); + cubeCards.add(new DraftCube.CardIdentity("Razaketh, the Foulblooded", "")); + cubeCards.add(new DraftCube.CardIdentity("Razorverge Thicket", "")); + cubeCards.add(new DraftCube.CardIdentity("Read the Bones", "")); + cubeCards.add(new DraftCube.CardIdentity("Reanimate", "")); + cubeCards.add(new DraftCube.CardIdentity("Reckless Bushwhacker", "")); + cubeCards.add(new DraftCube.CardIdentity("Reclamation Sage", "")); + cubeCards.add(new DraftCube.CardIdentity("Recruiter of the Guard", "")); + cubeCards.add(new DraftCube.CardIdentity("Recurring Nightmare", "")); + cubeCards.add(new DraftCube.CardIdentity("Reflecting Pool", "")); + cubeCards.add(new DraftCube.CardIdentity("Reflector Mage", "")); + cubeCards.add(new DraftCube.CardIdentity("Regrowth", "")); + cubeCards.add(new DraftCube.CardIdentity("Rekindling Phoenix", "")); + cubeCards.add(new DraftCube.CardIdentity("Relic of Progenitus", "")); + cubeCards.add(new DraftCube.CardIdentity("Remand", "")); + cubeCards.add(new DraftCube.CardIdentity("Remove Soul", "")); + cubeCards.add(new DraftCube.CardIdentity("Repeal", "")); + cubeCards.add(new DraftCube.CardIdentity("Restoration Angel", "")); + cubeCards.add(new DraftCube.CardIdentity("Reveillark", "")); + cubeCards.add(new DraftCube.CardIdentity("Rift Bolt", "")); + cubeCards.add(new DraftCube.CardIdentity("Riftwing Cloudskate", "")); + cubeCards.add(new DraftCube.CardIdentity("Rishadan Port", "")); + cubeCards.add(new DraftCube.CardIdentity("Rishkar, Peema Renegade", "")); + cubeCards.add(new DraftCube.CardIdentity("Roast", "")); + cubeCards.add(new DraftCube.CardIdentity("Rofellos, Llanowar Emissary", "")); + cubeCards.add(new DraftCube.CardIdentity("Rootbound Crag", "")); + cubeCards.add(new DraftCube.CardIdentity("Rune-Scarred Demon", "")); + cubeCards.add(new DraftCube.CardIdentity("Sacred Foundry", "")); + cubeCards.add(new DraftCube.CardIdentity("Sakura-Tribe Elder", "")); + cubeCards.add(new DraftCube.CardIdentity("Sarkhan, the Dragonspeaker", "")); + cubeCards.add(new DraftCube.CardIdentity("Savannah", "")); + cubeCards.add(new DraftCube.CardIdentity("Scalding Tarn", "")); + cubeCards.add(new DraftCube.CardIdentity("Scavenging Ooze", "")); + cubeCards.add(new DraftCube.CardIdentity("Scrapheap Scrounger", "")); + cubeCards.add(new DraftCube.CardIdentity("Scrubland", "")); + cubeCards.add(new DraftCube.CardIdentity("Sea Gate Oracle", "")); + cubeCards.add(new DraftCube.CardIdentity("Seachrome Coast", "")); + cubeCards.add(new DraftCube.CardIdentity("Seal of Fire", "")); + cubeCards.add(new DraftCube.CardIdentity("Search for Azcanta", "")); + cubeCards.add(new DraftCube.CardIdentity("Search for Tomorrow", "")); + cubeCards.add(new DraftCube.CardIdentity("Searing Spear", "")); + cubeCards.add(new DraftCube.CardIdentity("Secure the Wastes", "")); + cubeCards.add(new DraftCube.CardIdentity("Seeker of the Way", "")); + cubeCards.add(new DraftCube.CardIdentity("Selfless Spirit", "")); + cubeCards.add(new DraftCube.CardIdentity("Selvala, Heart of the Wilds", "")); + cubeCards.add(new DraftCube.CardIdentity("Sensei's Divining Top", "")); + cubeCards.add(new DraftCube.CardIdentity("Serendib Efreet", "")); + cubeCards.add(new DraftCube.CardIdentity("Serum Visions", "")); + cubeCards.add(new DraftCube.CardIdentity("Settle the Wreckage", "")); + cubeCards.add(new DraftCube.CardIdentity("Shaman of Forgotten Ways", "")); + cubeCards.add(new DraftCube.CardIdentity("Shambling Vent", "")); + cubeCards.add(new DraftCube.CardIdentity("Shardless Agent", "")); + cubeCards.add(new DraftCube.CardIdentity("Shelldock Isle", "")); + cubeCards.add(new DraftCube.CardIdentity("Sheoldred, Whispering One", "")); + cubeCards.add(new DraftCube.CardIdentity("Show and Tell", "")); + cubeCards.add(new DraftCube.CardIdentity("Shriekmaw", "")); + cubeCards.add(new DraftCube.CardIdentity("Siege-Gang Commander", "")); + cubeCards.add(new DraftCube.CardIdentity("Silverblade Paladin", "")); + cubeCards.add(new DraftCube.CardIdentity("Skinrender", "")); + cubeCards.add(new DraftCube.CardIdentity("Skymarcher Aspirant", "")); + cubeCards.add(new DraftCube.CardIdentity("Slagstorm", "")); + cubeCards.add(new DraftCube.CardIdentity("Slaughter Pact", "")); + cubeCards.add(new DraftCube.CardIdentity("Smuggler's Copter", "")); + cubeCards.add(new DraftCube.CardIdentity("Snapcaster Mage", "")); + cubeCards.add(new DraftCube.CardIdentity("Sneak Attack", "")); + cubeCards.add(new DraftCube.CardIdentity("Solemn Simulacrum", "")); + cubeCards.add(new DraftCube.CardIdentity("Song of the Dryads", "")); + cubeCards.add(new DraftCube.CardIdentity("Sorin Markov", "")); + cubeCards.add(new DraftCube.CardIdentity("Sorin, Solemn Visitor", "")); + cubeCards.add(new DraftCube.CardIdentity("Soulfire Grand Master", "")); + cubeCards.add(new DraftCube.CardIdentity("Sower of Temptation", "")); + cubeCards.add(new DraftCube.CardIdentity("Spear of Heliod", "")); + cubeCards.add(new DraftCube.CardIdentity("Spectral Procession", "")); + cubeCards.add(new DraftCube.CardIdentity("Spell Queller", "")); + cubeCards.add(new DraftCube.CardIdentity("Spellskite", "")); + cubeCards.add(new DraftCube.CardIdentity("Sphinx's Revelation", "")); + cubeCards.add(new DraftCube.CardIdentity("Spirebluff Canal", "")); + cubeCards.add(new DraftCube.CardIdentity("Splinter Twin", "")); + cubeCards.add(new DraftCube.CardIdentity("Staggershock", "")); + cubeCards.add(new DraftCube.CardIdentity("Steam Vents", "")); + cubeCards.add(new DraftCube.CardIdentity("Stirring Wildwood", "")); + cubeCards.add(new DraftCube.CardIdentity("Stoke the Flames", "")); + cubeCards.add(new DraftCube.CardIdentity("Stomping Ground", "")); + cubeCards.add(new DraftCube.CardIdentity("Stoneforge Mystic", "")); + cubeCards.add(new DraftCube.CardIdentity("Stormbreath Dragon", "")); + cubeCards.add(new DraftCube.CardIdentity("Stormchaser Mage", "")); + cubeCards.add(new DraftCube.CardIdentity("Stratus Dancer", "")); + cubeCards.add(new DraftCube.CardIdentity("Stroke of Genius", "")); + cubeCards.add(new DraftCube.CardIdentity("Stromkirk Noble", "")); + cubeCards.add(new DraftCube.CardIdentity("Student of Warfare", "")); + cubeCards.add(new DraftCube.CardIdentity("Sublime Archangel", "")); + cubeCards.add(new DraftCube.CardIdentity("Sulfur Falls", "")); + cubeCards.add(new DraftCube.CardIdentity("Sun Titan", "")); + cubeCards.add(new DraftCube.CardIdentity("Sundering Titan", "")); + cubeCards.add(new DraftCube.CardIdentity("Sunpetal Grove", "")); + cubeCards.add(new DraftCube.CardIdentity("Supreme Verdict", "")); + cubeCards.add(new DraftCube.CardIdentity("Surrak, the Hunt Caller", "")); + cubeCards.add(new DraftCube.CardIdentity("Sword of Body and Mind", "")); + cubeCards.add(new DraftCube.CardIdentity("Sword of Feast and Famine", "")); + cubeCards.add(new DraftCube.CardIdentity("Sword of Fire and Ice", "")); + cubeCards.add(new DraftCube.CardIdentity("Sword of Light and Shadow", "")); + cubeCards.add(new DraftCube.CardIdentity("Sword of War and Peace", "")); + cubeCards.add(new DraftCube.CardIdentity("Swords to Plowshares", "")); + cubeCards.add(new DraftCube.CardIdentity("Sylvan Advocate", "")); + cubeCards.add(new DraftCube.CardIdentity("Sylvan Caryatid", "")); + cubeCards.add(new DraftCube.CardIdentity("Sylvan Library", "")); + cubeCards.add(new DraftCube.CardIdentity("Taiga", "")); + cubeCards.add(new DraftCube.CardIdentity("Talrand, Sky Summoner", "")); + cubeCards.add(new DraftCube.CardIdentity("Tamiyo, the Moon Sage", "")); + cubeCards.add(new DraftCube.CardIdentity("Tangle Wire", "")); + cubeCards.add(new DraftCube.CardIdentity("Tarmogoyf", "")); + cubeCards.add(new DraftCube.CardIdentity("Tasigur, the Golden Fang", "")); + cubeCards.add(new DraftCube.CardIdentity("Teferi, Mage of Zhalfir", "")); + cubeCards.add(new DraftCube.CardIdentity("Temple Garden", "")); + cubeCards.add(new DraftCube.CardIdentity("Terastodon", "")); + cubeCards.add(new DraftCube.CardIdentity("Terminate", "")); + cubeCards.add(new DraftCube.CardIdentity("Terminus", "")); + cubeCards.add(new DraftCube.CardIdentity("Tetzimoc, Primal Death", "")); + cubeCards.add(new DraftCube.CardIdentity("Thalia, Guardian of Thraben", "")); + cubeCards.add(new DraftCube.CardIdentity("Thalia, Heretic Cathar", "")); + cubeCards.add(new DraftCube.CardIdentity("Thassa, God of the Sea", "")); + cubeCards.add(new DraftCube.CardIdentity("The Scarab God", "")); + cubeCards.add(new DraftCube.CardIdentity("Thing in the Ice", "")); + cubeCards.add(new DraftCube.CardIdentity("Thirst for Knowledge", "")); + cubeCards.add(new DraftCube.CardIdentity("Thoughtseize", "")); + cubeCards.add(new DraftCube.CardIdentity("Thragtusk", "")); + cubeCards.add(new DraftCube.CardIdentity("Thrun, the Last Troll", "")); + cubeCards.add(new DraftCube.CardIdentity("Thunderbreak Regent", "")); + cubeCards.add(new DraftCube.CardIdentity("Thundermaw Hellkite", "")); + cubeCards.add(new DraftCube.CardIdentity("Tidehollow Sculler", "")); + cubeCards.add(new DraftCube.CardIdentity("Time Warp", "")); + cubeCards.add(new DraftCube.CardIdentity("Tireless Tracker", "")); + cubeCards.add(new DraftCube.CardIdentity("Tooth and Nail", "")); + cubeCards.add(new DraftCube.CardIdentity("Torch Fiend", "")); + cubeCards.add(new DraftCube.CardIdentity("Tower of the Magistrate", "")); + cubeCards.add(new DraftCube.CardIdentity("Toxic Deluge", "")); + cubeCards.add(new DraftCube.CardIdentity("Treachery", "")); + cubeCards.add(new DraftCube.CardIdentity("Tropical Island", "")); + cubeCards.add(new DraftCube.CardIdentity("Trygon Predator", "")); + cubeCards.add(new DraftCube.CardIdentity("Tundra", "")); + cubeCards.add(new DraftCube.CardIdentity("Ugin, the Spirit Dragon", "")); + cubeCards.add(new DraftCube.CardIdentity("Ulamog, the Ceaseless Hunger", "")); + cubeCards.add(new DraftCube.CardIdentity("Ultimate Price", "")); + cubeCards.add(new DraftCube.CardIdentity("Umezawa's Jitte", "")); + cubeCards.add(new DraftCube.CardIdentity("Unburial Rites", "")); + cubeCards.add(new DraftCube.CardIdentity("Underground Sea", "")); + cubeCards.add(new DraftCube.CardIdentity("Underworld Connections", "")); + cubeCards.add(new DraftCube.CardIdentity("Unexpectedly Absent", "")); + cubeCards.add(new DraftCube.CardIdentity("Upheaval", "")); + cubeCards.add(new DraftCube.CardIdentity("Utopia Sprawl", "")); + cubeCards.add(new DraftCube.CardIdentity("Vampire Hexmage", "")); + cubeCards.add(new DraftCube.CardIdentity("Vampire Nighthawk", "")); + cubeCards.add(new DraftCube.CardIdentity("Vendilion Clique", "")); + cubeCards.add(new DraftCube.CardIdentity("Venser, Shaper Savant", "")); + cubeCards.add(new DraftCube.CardIdentity("Verdant Catacombs", "")); + cubeCards.add(new DraftCube.CardIdentity("Verdurous Gearhulk", "")); + cubeCards.add(new DraftCube.CardIdentity("Vindicate", "")); + cubeCards.add(new DraftCube.CardIdentity("Volcanic Island", "")); + cubeCards.add(new DraftCube.CardIdentity("Volrath's Stronghold", "")); + cubeCards.add(new DraftCube.CardIdentity("Vraska's Contempt", "")); + cubeCards.add(new DraftCube.CardIdentity("Vraska, Relic Seeker", "")); + cubeCards.add(new DraftCube.CardIdentity("Walking Ballista", "")); + cubeCards.add(new DraftCube.CardIdentity("Wall of Blossoms", "")); + cubeCards.add(new DraftCube.CardIdentity("Wall of Omens", "")); + cubeCards.add(new DraftCube.CardIdentity("Wandering Fumarole", "")); + cubeCards.add(new DraftCube.CardIdentity("Wasteland", "")); + cubeCards.add(new DraftCube.CardIdentity("Watery Grave", "")); + cubeCards.add(new DraftCube.CardIdentity("Whip of Erebos", "")); + cubeCards.add(new DraftCube.CardIdentity("Whirler Rogue", "")); + cubeCards.add(new DraftCube.CardIdentity("Whisperwood Elemental", "")); + cubeCards.add(new DraftCube.CardIdentity("Wickerbough Elder", "")); + cubeCards.add(new DraftCube.CardIdentity("Windbrisk Heights", "")); + cubeCards.add(new DraftCube.CardIdentity("Windswept Heath", "")); + cubeCards.add(new DraftCube.CardIdentity("Wolfir Silverheart", "")); + cubeCards.add(new DraftCube.CardIdentity("Wood Elves", "")); + cubeCards.add(new DraftCube.CardIdentity("Wooded Foothills", "")); + cubeCards.add(new DraftCube.CardIdentity("Woodfall Primus", "")); + cubeCards.add(new DraftCube.CardIdentity("Woodland Cemetery", "")); + cubeCards.add(new DraftCube.CardIdentity("Wrath of God", "")); + cubeCards.add(new DraftCube.CardIdentity("Wretched Confluence", "")); + cubeCards.add(new DraftCube.CardIdentity("Wurmcoil Engine", "")); + cubeCards.add(new DraftCube.CardIdentity("Xenagos, the Reveler", "")); + cubeCards.add(new DraftCube.CardIdentity("Yavimaya Elder", "")); + cubeCards.add(new DraftCube.CardIdentity("Young Pyromancer", "")); + cubeCards.add(new DraftCube.CardIdentity("Zealous Conscripts", "")); + } +} diff --git a/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml b/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml index de7da29dd86..5fc3a0b2657 100644 --- a/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml +++ b/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-tournament-constructed diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml b/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml index 0fcb222cdad..175499da7a7 100644 --- a/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml +++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.27 + 1.4.28 mage-tournament-sealed diff --git a/Mage.Server.Plugins/pom.xml b/Mage.Server.Plugins/pom.xml index a59faa7dbf4..0c1231654df 100644 --- a/Mage.Server.Plugins/pom.xml +++ b/Mage.Server.Plugins/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.27 + 1.4.28 mage-server-plugins diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index aceb3bdd24d..f1e74d0af73 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -110,12 +110,13 @@ - - - - - - + + + + + + + diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index f756f0f7c17..23914ed6ca2 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.27 + 1.4.28 mage-server @@ -76,6 +76,12 @@ ${project.version} runtime + + org.apache.commons + commons-compress + 1.16.1 + + ${project.groupId} mage-game-commanderfreeforall diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml index 9bfea45584d..e640228e8a3 100644 --- a/Mage.Server/release/config/config.xml +++ b/Mage.Server/release/config/config.xml @@ -107,12 +107,13 @@ - - - - - - + + + + + + + diff --git a/Mage.Sets/pom.xml b/Mage.Sets/pom.xml index 384a16fb515..94c14483fa7 100644 --- a/Mage.Sets/pom.xml +++ b/Mage.Sets/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.27 + 1.4.28 org.mage diff --git a/Mage.Sets/src/mage/cards/a/AnimationModule.java b/Mage.Sets/src/mage/cards/a/AnimationModule.java index 2c154badffb..247759bda57 100644 --- a/Mage.Sets/src/mage/cards/a/AnimationModule.java +++ b/Mage.Sets/src/mage/cards/a/AnimationModule.java @@ -63,7 +63,7 @@ public class AnimationModule extends CardImpl { public AnimationModule(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); - // Whenever one or more +1/+1 counters are placed on a permanent you control, you may pay {1}. If you do, create a 1/1 colorless Servo artifact creature token. + // Whenever one or more +1/+1 counters are put on a permanent you control, you may pay {1}. If you do, create a 1/1 colorless Servo artifact creature token. this.addAbility(new AnimationModuleTriggeredAbility()); // {3}, {T}: Choose a counter on target permanent or player. Give that permanent or player another counter of that kind. @@ -117,7 +117,7 @@ class AnimationModuleTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever one or more +1/+1 counters are placed on a permanent you control, you may pay {1}. If you do, create a 1/1 colorless Servo artifact creature token."; + return "Whenever one or more +1/+1 counters are put on a permanent you control, you may pay {1}. If you do, create a 1/1 colorless Servo artifact creature token."; } } diff --git a/Mage.Sets/src/mage/cards/a/ArchwingDragon.java b/Mage.Sets/src/mage/cards/a/ArchwingDragon.java index c2f8751e51a..1067f47a1a9 100644 --- a/Mage.Sets/src/mage/cards/a/ArchwingDragon.java +++ b/Mage.Sets/src/mage/cards/a/ArchwingDragon.java @@ -29,7 +29,7 @@ package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.abilities.common.OnEventTriggeredAbility; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; @@ -37,7 +37,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.game.events.GameEvent; +import mage.constants.TargetController; /** * @@ -46,7 +46,7 @@ import mage.game.events.GameEvent; public class ArchwingDragon extends CardImpl { public ArchwingDragon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); this.subtype.add(SubType.DRAGON); this.power = new MageInt(4); @@ -54,8 +54,10 @@ public class ArchwingDragon extends CardImpl { this.addAbility(FlyingAbility.getInstance()); this.addAbility(HasteAbility.getInstance()); + // At the beginning of the end step, return Archwing Dragon to its owner's hand. - this.addAbility(new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", new ReturnToHandSourceEffect(true), false)); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new ReturnToHandSourceEffect(true), TargetController.ANY, false)); + } public ArchwingDragon(final ArchwingDragon card) { diff --git a/Mage.Sets/src/mage/cards/b/BloodcrazedHoplite.java b/Mage.Sets/src/mage/cards/b/BloodcrazedHoplite.java index 4ea155477da..93accc1ed00 100644 --- a/Mage.Sets/src/mage/cards/b/BloodcrazedHoplite.java +++ b/Mage.Sets/src/mage/cards/b/BloodcrazedHoplite.java @@ -69,7 +69,7 @@ public class BloodcrazedHoplite extends CardImpl { // Heroic - Whenever you cast a spell that targets Bloodcrazed Hoplite, put a +1/+1 counter on it. this.addAbility(new HeroicAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(), false))); - // Whenever a +1/+1 counter is placed on Bloodcrazed Hoplite, remove a +1/+1 counter from target creature an opponent controls. + // Whenever a +1/+1 counter is put on Bloodcrazed Hoplite, remove a +1/+1 counter from target creature an opponent controls. Ability ability = new BloodcrazedHopliteTriggeredAbility(); ability.addTarget(new TargetCreaturePermanent(filter)); this.addAbility(ability); @@ -112,6 +112,6 @@ class BloodcrazedHopliteTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a +1/+1 counter is placed on {this}, " + super.getRule(); + return "Whenever a +1/+1 counter is put on {this}, " + super.getRule(); } } diff --git a/Mage.Sets/src/mage/cards/b/BrothersYamazaki.java b/Mage.Sets/src/mage/cards/b/BrothersYamazaki.java index c5f843ca52a..761add31ae8 100644 --- a/Mage.Sets/src/mage/cards/b/BrothersYamazaki.java +++ b/Mage.Sets/src/mage/cards/b/BrothersYamazaki.java @@ -43,6 +43,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -56,6 +57,7 @@ public class BrothersYamazaki extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); static { + filter.add(new AnotherPredicate()); filter.add(new NamePredicate("Brothers Yamazaki")); } diff --git a/Mage.Sets/src/mage/cards/b/BuildersBane.java b/Mage.Sets/src/mage/cards/b/BuildersBane.java new file mode 100644 index 00000000000..2dc7950731e --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BuildersBane.java @@ -0,0 +1,121 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.b; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetArtifactPermanent; + +/** + * + * @author sinsedrix + */ +public class BuildersBane extends CardImpl { + + public BuildersBane(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{R}"); + + // Destroy X target artifacts. Builder's Bane deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way. + this.getSpellAbility().addTarget(new TargetArtifactPermanent()); + this.getSpellAbility().addEffect(new BuildersBaneEffect()); + } + + @Override + public void adjustTargets(Ability ability, Game game) { + if (ability instanceof SpellAbility) { + ability.getTargets().clear(); + int xValue = ability.getManaCostsToPay().getX(); + ability.addTarget(new TargetArtifactPermanent(xValue, xValue)); + } + } + + public BuildersBane(final BuildersBane card) { + super(card); + } + + @Override + public BuildersBane copy() { + return new BuildersBane(this); + } +} + +class BuildersBaneEffect extends OneShotEffect { + + public BuildersBaneEffect() { + super(Outcome.DestroyPermanent); + this.staticText = "Destroy X target artifacts. {this} deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way"; + } + + public BuildersBaneEffect(final BuildersBaneEffect effect) { + super(effect); + } + + @Override + public BuildersBaneEffect copy() { + return new BuildersBaneEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Map destroyedArtifactPerPlayer = new HashMap<>(); + + // Destroy X target artifacts. + for (UUID targetID : this.targetPointer.getTargets(game, source)) { + Permanent permanent = game.getPermanent(targetID); + if (permanent != null) { + if (permanent.destroy(source.getSourceId(), game, false)) { + if (game.getState().getZone(permanent.getId()) == Zone.GRAVEYARD) { + destroyedArtifactPerPlayer.merge(permanent.getControllerId(), 1, Integer::sum); + } + } + } + } + + // Builder's Bane deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way. + for (Map.Entry entry : destroyedArtifactPerPlayer.entrySet()) { + Player player = game.getPlayer(entry.getKey()); + if(player != null) { + player.damage(entry.getValue(), source.getSourceId(), game, false, true); + } + } + + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CorpsejackMenace.java b/Mage.Sets/src/mage/cards/c/CorpsejackMenace.java index 57ea6563351..5e35c026b39 100644 --- a/Mage.Sets/src/mage/cards/c/CorpsejackMenace.java +++ b/Mage.Sets/src/mage/cards/c/CorpsejackMenace.java @@ -65,7 +65,7 @@ public class CorpsejackMenace extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // If one or more +1/+1 counters would be placed on a creature you control, twice that many +1/+1 counters are placed on it instead. + // If one or more +1/+1 counters would be put on a creature you control, twice that many +1/+1 counters are put on it instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CorpsejackMenaceReplacementEffect())); } @@ -84,7 +84,7 @@ class CorpsejackMenaceReplacementEffect extends ReplacementEffectImpl { CorpsejackMenaceReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false); - staticText = "If one or more +1/+1 counters would be placed on a creature you control, twice that many +1/+1 counters are placed on it instead"; + staticText = "If one or more +1/+1 counters would be put on a creature you control, twice that many +1/+1 counters are put on it instead"; } CorpsejackMenaceReplacementEffect(final CorpsejackMenaceReplacementEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/Crevasse.java b/Mage.Sets/src/mage/cards/c/Crevasse.java new file mode 100644 index 00000000000..05bd0daad78 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/Crevasse.java @@ -0,0 +1,91 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.c; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.AsThoughEffectType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; + +/** + * + * @author L_J + */ +public class Crevasse extends CardImpl { + + public Crevasse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + + // Creatures with mountainwalk can be blocked as though they didn't have mountainwalk. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CrevasseEffect())); + } + + public Crevasse(final Crevasse card) { + super(card); + } + + @Override + public Crevasse copy() { + return new Crevasse(this); + } +} + +class CrevasseEffect extends AsThoughEffectImpl { + + public CrevasseEffect() { + super(AsThoughEffectType.BLOCK_MOUNTAINWALK, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Creatures with mountainwalk can be blocked as though they didn't have mountainwalk"; + } + + public CrevasseEffect(final CrevasseEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public CrevasseEffect copy() { + return new CrevasseEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java b/Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java index fb4253deb67..51bb5df07da 100644 --- a/Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java +++ b/Mage.Sets/src/mage/cards/c/CurseOfTheSwine.java @@ -27,18 +27,22 @@ */ package mage.cards.c; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.CurseOfTheSwineBoarToken; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; /** @@ -58,7 +62,7 @@ public class CurseOfTheSwine extends CardImpl { @Override public void adjustTargets(Ability ability, Game game) { - if (ability instanceof SpellAbility) { + if (ability instanceof SpellAbility && ability.getAbilityType().equals(AbilityType.SPELL)) { ability.getTargets().clear(); ability.addTarget(new TargetCreaturePermanent(ability.getManaCostsToPay().getX())); } @@ -92,15 +96,24 @@ class CurseOfTheSwineEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { - Permanent creature = game.getPermanent(targetId); - if (creature != null) { - if (creature.moveToExile(null, null, source.getSourceId(), game) || creature.moveToZone(Zone.COMMAND, source.getSourceId(), game, false)) { - CurseOfTheSwineBoarToken swineToken = new CurseOfTheSwineBoarToken(); - swineToken.putOntoBattlefield(1, game, source.getSourceId(), creature.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Map playersWithTargets = new HashMap<>(); + for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { + Permanent creature = game.getPermanent(targetId); + if (creature != null) { + if (controller.moveCards(creature, Zone.EXILED, source, game)) { + playersWithTargets.put(creature.getControllerId(), playersWithTargets.getOrDefault(creature.getControllerId(), 0) + 1); + } } } + CurseOfTheSwineBoarToken swineToken = new CurseOfTheSwineBoarToken(); + for (UUID playerId : playersWithTargets.keySet()) { + swineToken.putOntoBattlefield(playersWithTargets.get(playerId), game, source.getSourceId(), playerId); + } + return true; } - return true; + return false; + } } diff --git a/Mage.Sets/src/mage/cards/d/Deadfall.java b/Mage.Sets/src/mage/cards/d/Deadfall.java new file mode 100644 index 00000000000..d1c67a8ee34 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/Deadfall.java @@ -0,0 +1,91 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.d; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.AsThoughEffectType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; + +/** + * + * @author L_J + */ +public class Deadfall extends CardImpl { + + public Deadfall(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + // Creatures with forestwalk can be blocked as though they didn't have forestwalk. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DeadfallEffect())); + } + + public Deadfall(final Deadfall card) { + super(card); + } + + @Override + public Deadfall copy() { + return new Deadfall(this); + } +} + +class DeadfallEffect extends AsThoughEffectImpl { + + public DeadfallEffect() { + super(AsThoughEffectType.BLOCK_FORESTWALK, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Creatures with forestwalk can be blocked as though they didn't have forestwalk"; + } + + public DeadfallEffect(final DeadfallEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public DeadfallEffect copy() { + return new DeadfallEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/d/DinosaurHunter.java b/Mage.Sets/src/mage/cards/d/DinosaurHunter.java index 3e763804225..66786460002 100644 --- a/Mage.Sets/src/mage/cards/d/DinosaurHunter.java +++ b/Mage.Sets/src/mage/cards/d/DinosaurHunter.java @@ -35,7 +35,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -53,7 +52,7 @@ public class DinosaurHunter extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // Whenever Dinosaur Hunter deals combat damage to a Dinosaur, destroy that creature. + // Whenever Dinosaur Hunter deals damage to a Dinosaur, destroy that creature. this.addAbility(new DinosaurHunterAbility()); } @@ -89,11 +88,10 @@ class DinosaurHunterAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (((DamageEvent) event).isCombatDamage() - && event.getSourceId().equals(getSourceId())) { + if (event.getSourceId().equals(getSourceId())) { Permanent targetPermanet = game.getPermanentOrLKIBattlefield(event.getTargetId()); if (targetPermanet.hasSubtype(SubType.DINOSAUR, game)) { - getEffects().get(0).setTargetPointer(new FixedTarget(targetPermanet, game)); + getEffects().setTargetPointer(new FixedTarget(targetPermanet, game)); return true; } } @@ -102,6 +100,6 @@ class DinosaurHunterAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever {this} deals combat damage to a Dinosaur, destroy that creature."; + return "Whenever {this} deals damage to a Dinosaur, destroy that creature."; } } diff --git a/Mage.Sets/src/mage/cards/d/DoublingSeason.java b/Mage.Sets/src/mage/cards/d/DoublingSeason.java index 15e0343f06d..0424e26c57b 100644 --- a/Mage.Sets/src/mage/cards/d/DoublingSeason.java +++ b/Mage.Sets/src/mage/cards/d/DoublingSeason.java @@ -54,7 +54,7 @@ public class DoublingSeason extends CardImpl { // If an effect would create one or more tokens under your control, it creates twice that many of those tokens instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CreateTwiceThatManyTokensEffect())); - // If an effect would place one or more counters on a permanent you control, it places twice that many of those counters on that permanent instead. + // If an effect would put one or more counters on a permanent you control, it puts twice that many of those counters on that permanent instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DoublingSeasonCounterEffect())); } @@ -75,7 +75,7 @@ class DoublingSeasonCounterEffect extends ReplacementEffectImpl { DoublingSeasonCounterEffect() { super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false); - staticText = "If an effect would place one or more counters on a permanent you control, it places twice that many of those counters on that permanent instead"; + staticText = "If an effect would put one or more counters on a permanent you control, it puts twice that many of those counters on that permanent instead"; } DoublingSeasonCounterEffect(final DoublingSeasonCounterEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/Dread.java b/Mage.Sets/src/mage/cards/d/Dread.java index d1bdce09529..a2ec93af042 100644 --- a/Mage.Sets/src/mage/cards/d/Dread.java +++ b/Mage.Sets/src/mage/cards/d/Dread.java @@ -31,7 +31,6 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.PutIntoGraveFromAnywhereSourceTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect; import mage.abilities.keyword.FearAbility; @@ -105,11 +104,7 @@ class DreadTriggeredAbility extends TriggeredAbilityImpl { if (event.getPlayerId().equals(this.getControllerId())) { Permanent permanent = game.getPermanent(event.getSourceId()); if (permanent != null && permanent.isCreature()) { - for (Effect effect : this.getEffects()) { - if (effect instanceof DestroyTargetEffect) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); - } - } + this.getEffects().setTargetPointer(new FixedTarget(permanent, game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/e/EnduringScalelord.java b/Mage.Sets/src/mage/cards/e/EnduringScalelord.java index a591f0b15dc..c5e2fa37889 100644 --- a/Mage.Sets/src/mage/cards/e/EnduringScalelord.java +++ b/Mage.Sets/src/mage/cards/e/EnduringScalelord.java @@ -57,7 +57,7 @@ public class EnduringScalelord extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // Whenever one or more +1/+1 counters are place on another creature you control, you may put a +1/+1 counter on Enduring Scaleguard. + // Whenever one or more +1/+1 counters are put on another creature you control, you may put a +1/+1 counter on Enduring Scaleguard. this.addAbility(new EnduringScalelordTriggeredAbility()); } @@ -109,6 +109,6 @@ class EnduringScalelordTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever one or more +1/+1 counters are place on another creature you control, you may put a +1/+1 counter on {this}."; + return "Whenever one or more +1/+1 counters are put on another creature you control, you may put a +1/+1 counter on {this}."; } } diff --git a/Mage.Sets/src/mage/cards/e/Exhume.java b/Mage.Sets/src/mage/cards/e/Exhume.java index 678d17dddcb..a06a50309e0 100644 --- a/Mage.Sets/src/mage/cards/e/Exhume.java +++ b/Mage.Sets/src/mage/cards/e/Exhume.java @@ -94,6 +94,7 @@ class ExhumeEffect extends OneShotEffect { FilterCreatureCard filterCreatureCard = new FilterCreatureCard("creature card from your graveyard"); filterCreatureCard.add(new OwnerIdPredicate(playerId)); TargetCardInGraveyard target = new TargetCardInGraveyard(filterCreatureCard); + target.setNotTarget(true); if (target.canChoose(playerId, game) && player.chooseTarget(outcome, target, source, game)) { Card card = game.getCard(target.getFirstTarget()); diff --git a/Mage.Sets/src/mage/cards/f/FairgroundsTrumpeter.java b/Mage.Sets/src/mage/cards/f/FairgroundsTrumpeter.java index 371aa107a7c..27ed3beda51 100644 --- a/Mage.Sets/src/mage/cards/f/FairgroundsTrumpeter.java +++ b/Mage.Sets/src/mage/cards/f/FairgroundsTrumpeter.java @@ -59,11 +59,11 @@ public class FairgroundsTrumpeter extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // At the beginning of each end step, if a +1/+1 counter was placed on a permanent under your control this turn, put a +1/+1 counter on Fairgrounds Trumpeter. + // At the beginning of each end step, if a +1/+1 counter was put on a permanent under your control this turn, put a +1/+1 counter on Fairgrounds Trumpeter. this.addAbility(new ConditionalTriggeredAbility(new BeginningOfEndStepTriggeredAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance()), TargetController.ANY, false), FairgroundsTrumpeterCondition.instance, - "At the beginning of each end step, if a +1/+1 counter was placed on a permanent under your control this turn, put a +1/+1 counter on {this}."), + "At the beginning of each end step, if a +1/+1 counter was put on a permanent under your control this turn, put a +1/+1 counter on {this}."), new FairgroundsTrumpeterWatcher()); } @@ -89,7 +89,7 @@ enum FairgroundsTrumpeterCondition implements Condition { @Override public String toString() { - return "if a +1/+1 counter was placed on a permanent under your control this turn"; + return "if a +1/+1 counter was put on a permanent under your control this turn"; } } diff --git a/Mage.Sets/src/mage/cards/f/FathomMage.java b/Mage.Sets/src/mage/cards/f/FathomMage.java index dd60e4a70d1..58a99d3219c 100644 --- a/Mage.Sets/src/mage/cards/f/FathomMage.java +++ b/Mage.Sets/src/mage/cards/f/FathomMage.java @@ -61,7 +61,7 @@ public class FathomMage extends CardImpl { // has greater power or toughness than this creature, put a +1/+1 counter on this creature.) this.addAbility(new EvolveAbility()); - //Whenever a +1/+1 counter is placed on Fathom Mage, you may draw a card. + //Whenever a +1/+1 counter is put on Fathom Mage, you may draw a card. this.addAbility(new FathomMageTriggeredAbility()); } @@ -102,6 +102,6 @@ class FathomMageTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a +1/+1 counter is placed on {this}, " + super.getRule(); + return "Whenever a +1/+1 counter is put on {this}, " + super.getRule(); } } diff --git a/Mage.Sets/src/mage/cards/f/FlourishingDefenses.java b/Mage.Sets/src/mage/cards/f/FlourishingDefenses.java index a902410c388..7f13fbdc42a 100644 --- a/Mage.Sets/src/mage/cards/f/FlourishingDefenses.java +++ b/Mage.Sets/src/mage/cards/f/FlourishingDefenses.java @@ -50,7 +50,7 @@ public class FlourishingDefenses extends CardImpl { public FlourishingDefenses(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{G}"); - // Whenever a -1/-1 counter is placed on a creature, you may create a 1/1 green Elf Warrior creature token. + // Whenever a -1/-1 counter is put on a creature, you may create a 1/1 green Elf Warrior creature token. this.addAbility(new FlourishingDefensesTriggeredAbility()); } @@ -98,6 +98,6 @@ class FlourishingDefensesTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a -1/-1 counter is placed on a creature, you may create a 1/1 green Elf Warrior creature token."; + return "Whenever a -1/-1 counter is put on a creature, you may create a 1/1 green Elf Warrior creature token."; } } diff --git a/Mage.Sets/src/mage/cards/g/GiltLeafArchdruid.java b/Mage.Sets/src/mage/cards/g/GiltLeafArchdruid.java index ac375927a73..99a265d6210 100644 --- a/Mage.Sets/src/mage/cards/g/GiltLeafArchdruid.java +++ b/Mage.Sets/src/mage/cards/g/GiltLeafArchdruid.java @@ -30,12 +30,13 @@ package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.common.TapTargetCost; -import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -48,6 +49,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; /** * @@ -62,7 +64,7 @@ public class GiltLeafArchdruid extends CardImpl { } public GiltLeafArchdruid(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.DRUID); @@ -73,7 +75,7 @@ public class GiltLeafArchdruid extends CardImpl { this.addAbility(new SpellCastControllerTriggeredAbility(new DrawCardSourceControllerEffect(1), filterSpell, true)); // Tap seven untapped Druids you control: Gain control of all lands target player controls. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainControlAllLandsEffect(Duration.EndOfGame), new TapTargetCost(new TargetControlledCreaturePermanent(7, 7, new FilterControlledCreaturePermanent(SubType.DRUID, "Druids you control"), true))); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GiltLeafArchdruidEffect(), new TapTargetCost(new TargetControlledCreaturePermanent(7, 7, new FilterControlledCreaturePermanent(SubType.DRUID, "Druids you control"), true))); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } @@ -88,38 +90,36 @@ public class GiltLeafArchdruid extends CardImpl { } } -class GainControlAllLandsEffect extends ContinuousEffectImpl { +class GiltLeafArchdruidEffect extends OneShotEffect { - public GainControlAllLandsEffect(Duration duration) { - super(duration, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); + public GiltLeafArchdruidEffect() { + super(Outcome.GainControl); + this.staticText = "gain control of all lands target player controls"; } - public GainControlAllLandsEffect(final GainControlAllLandsEffect effect) { + public GiltLeafArchdruidEffect(final GiltLeafArchdruidEffect effect) { super(effect); } @Override - public GainControlAllLandsEffect copy() { - return new GainControlAllLandsEffect(this); + public GiltLeafArchdruidEffect copy() { + return new GiltLeafArchdruidEffect(this); } @Override public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source)); - if (targetPlayer != null && targetPlayer.isInGame()) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_LANDS, targetPointer.getFirst(game, source), game)) { + if (controller != null && targetPlayer != null) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_LANDS, targetPlayer.getId(), game)) { if (permanent != null) { - permanent.changeControllerId(source.getControllerId(), game); + ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, true); + effect.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effect, source); } } - } else { - discard(); + return true; } - return true; - } - - @Override - public String getText(Mode mode) { - return "Gain control of all lands target player controls"; + return false; } } diff --git a/Mage.Sets/src/mage/cards/g/GostaDirk.java b/Mage.Sets/src/mage/cards/g/GostaDirk.java new file mode 100644 index 00000000000..4ca1aa3283d --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GostaDirk.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.cards.g; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author L_J + */ +public class GostaDirk extends CardImpl { + + public GostaDirk(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}{U}{U}"); + addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Creatures with islandwalk can be blocked as though they didn't have islandwalk. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GostaDirkEffect())); + } + + public GostaDirk(final GostaDirk card) { + super(card); + } + + @Override + public GostaDirk copy() { + return new GostaDirk(this); + } +} + +class GostaDirkEffect extends AsThoughEffectImpl { + + public GostaDirkEffect() { + super(AsThoughEffectType.BLOCK_ISLANDWALK, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Creatures with islandwalk can be blocked as though they didn't have islandwalk"; + } + + public GostaDirkEffect(final GostaDirkEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public GostaDirkEffect copy() { + return new GostaDirkEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GreatWall.java b/Mage.Sets/src/mage/cards/g/GreatWall.java new file mode 100644 index 00000000000..becca2448b8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GreatWall.java @@ -0,0 +1,91 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.g; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.AsThoughEffectType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; + +/** + * + * @author L_J + */ +public class GreatWall extends CardImpl { + + public GreatWall(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // Creatures with plainswalk can be blocked as though they didn't have plainswalk. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GreatWallEffect())); + } + + public GreatWall(final GreatWall card) { + super(card); + } + + @Override + public GreatWall copy() { + return new GreatWall(this); + } +} + +class GreatWallEffect extends AsThoughEffectImpl { + + public GreatWallEffect() { + super(AsThoughEffectType.BLOCK_PLAINSWALK, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Creatures with plainswalk can be blocked as though they didn't have plainswalk"; + } + + public GreatWallEffect(final GreatWallEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public GreatWallEffect copy() { + return new GreatWallEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HardenedScales.java b/Mage.Sets/src/mage/cards/h/HardenedScales.java index 3268c0875ec..a1197feeae9 100644 --- a/Mage.Sets/src/mage/cards/h/HardenedScales.java +++ b/Mage.Sets/src/mage/cards/h/HardenedScales.java @@ -52,7 +52,7 @@ public class HardenedScales extends CardImpl { public HardenedScales(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{G}"); - // If one or more +1/+1 counters would be placed on a creature you control, that many plus one +1/+1 counters are placed on it instead. + // If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on it instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new HardenedScalesEffect())); } @@ -71,7 +71,7 @@ class HardenedScalesEffect extends ReplacementEffectImpl { HardenedScalesEffect() { super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false); - staticText = "If one or more +1/+1 counters would be placed on a creature you control, that many plus one +1/+1 counters are placed on it instead"; + staticText = "If one or more +1/+1 counters would be put on a creature you control, that many plus one +1/+1 counters are put on it instead"; } HardenedScalesEffect(final HardenedScalesEffect effect) { diff --git a/Mage.Sets/src/mage/cards/h/HeatShimmer.java b/Mage.Sets/src/mage/cards/h/HeatShimmer.java index 68967ad6ab0..80fb54d1090 100644 --- a/Mage.Sets/src/mage/cards/h/HeatShimmer.java +++ b/Mage.Sets/src/mage/cards/h/HeatShimmer.java @@ -29,10 +29,9 @@ package mage.cards.h; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility; -import mage.abilities.effects.Effect; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; import mage.cards.CardImpl; @@ -52,7 +51,7 @@ import mage.target.targetpointer.FixedTarget; public class HeatShimmer extends CardImpl { public HeatShimmer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); // Create a token that's a copy of target creature. That token has haste and "At the beginning of the end step, exile this permanent." this.getSpellAbility().addEffect(new HeatShimmerEffect()); @@ -89,15 +88,15 @@ class HeatShimmerEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source)); - if (controller != null && permanent != null) { + if (controller != null + && permanent != null) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); effect.setTargetPointer(new FixedTarget(permanent, game)); effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanent()) { - Effect exileEffect = new ExileTargetEffect(); - exileEffect.setTargetPointer(new FixedTarget(addedToken, game)); - new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfTurnStepPostDelayedTriggeredAbility(exileEffect), false).apply(game, source); - } + ExileTargetEffect exileEffect = new ExileTargetEffect(); + exileEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanent().get(0), game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + game.addDelayedTriggeredAbility(delayedAbility, source); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/h/HotSoup.java b/Mage.Sets/src/mage/cards/h/HotSoup.java index 149fc23d683..969b778c722 100644 --- a/Mage.Sets/src/mage/cards/h/HotSoup.java +++ b/Mage.Sets/src/mage/cards/h/HotSoup.java @@ -104,10 +104,7 @@ class HotSoupTriggeredAbility extends TriggeredAbilityImpl { Permanent equipment = game.getPermanent(this.getSourceId()); if (equipment != null && equipment.getAttachedTo() != null) { if (Objects.equals(event.getTargetId(), equipment.getAttachedTo())) { - for(Effect effect : this.getEffects()) - { - effect.setTargetPointer(new FixedTarget(equipment.getAttachedTo())); - } + this.getEffects().setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game)); return true; } } @@ -118,4 +115,4 @@ class HotSoupTriggeredAbility extends TriggeredAbilityImpl { public String getRule() { return "Whenever equipped creature is dealt damage, " + super.getRule(); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/k/KheruLichLord.java b/Mage.Sets/src/mage/cards/k/KheruLichLord.java index 34086bd9fed..8598e601e0b 100644 --- a/Mage.Sets/src/mage/cards/k/KheruLichLord.java +++ b/Mage.Sets/src/mage/cards/k/KheruLichLord.java @@ -49,9 +49,9 @@ import mage.cards.CardSetInfo; import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; @@ -69,7 +69,7 @@ import mage.target.targetpointer.FixedTarget; public class KheruLichLord extends CardImpl { public KheruLichLord(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{G}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{G}{U}"); this.subtype.add(SubType.ZOMBIE); this.subtype.add(SubType.WIZARD); @@ -166,14 +166,8 @@ class KheruLichLordReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Card card = game.getCard(getTargetPointer().getFirst(game, source)); - if (card != null) { - controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, game.getState().getZone(card.getId()), true); - } - } - return true; + ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + return false; } @Override diff --git a/Mage.Sets/src/mage/cards/l/LordMagnus.java b/Mage.Sets/src/mage/cards/l/LordMagnus.java new file mode 100644 index 00000000000..b0400022a74 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LordMagnus.java @@ -0,0 +1,129 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.l; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author L_J + */ +public class LordMagnus extends CardImpl { + + public LordMagnus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{W}{W}"); + addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DRUID); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Creatures with plainswalk can be blocked as though they didn't have plainswalk. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LordMagnusFirstEffect())); + + // Creatures with forestwalk can be blocked as though they didn't have forestwalk. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LordMagnusSecondEffect())); + } + + public LordMagnus(final LordMagnus card) { + super(card); + } + + @Override + public LordMagnus copy() { + return new LordMagnus(this); + } +} + +class LordMagnusFirstEffect extends AsThoughEffectImpl { + + public LordMagnusFirstEffect() { + super(AsThoughEffectType.BLOCK_PLAINSWALK, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Creatures with plainswalk can be blocked as though they didn't have plainswalk"; + } + + public LordMagnusFirstEffect(final LordMagnusFirstEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public LordMagnusFirstEffect copy() { + return new LordMagnusFirstEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return true; + } +} + +class LordMagnusSecondEffect extends AsThoughEffectImpl { + + public LordMagnusSecondEffect() { + super(AsThoughEffectType.BLOCK_FORESTWALK, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Creatures with forestwalk can be blocked as though they didn't have forestwalk"; + } + + public LordMagnusSecondEffect(final LordMagnusSecondEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public LordMagnusSecondEffect copy() { + return new LordMagnusSecondEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java b/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java index f5fc8bd06e9..9797459684f 100644 --- a/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java +++ b/Mage.Sets/src/mage/cards/m/MeliraSylvokOutcast.java @@ -63,7 +63,7 @@ public class MeliraSylvokOutcast extends CardImpl { // You can't get poison counters. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MeliraSylvokOutcastEffect())); - // Creatures you control can't have -1/-1 counters placed on them. + // Creatures you control can't have -1/-1 counters put on them. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MeliraSylvokOutcastEffect2())); // Creatures your opponents control lose infect. @@ -118,7 +118,7 @@ class MeliraSylvokOutcastEffect2 extends ReplacementEffectImpl { public MeliraSylvokOutcastEffect2() { super(Duration.WhileOnBattlefield, Outcome.PreventDamage); - staticText = "Creatures you control can't have -1/-1 counters placed on them"; + staticText = "Creatures you control can't have -1/-1 counters put on them"; } public MeliraSylvokOutcastEffect2(final MeliraSylvokOutcastEffect2 effect) { diff --git a/Mage.Sets/src/mage/cards/m/MelirasKeepers.java b/Mage.Sets/src/mage/cards/m/MelirasKeepers.java index 82d8887002f..aab2edfbbcc 100644 --- a/Mage.Sets/src/mage/cards/m/MelirasKeepers.java +++ b/Mage.Sets/src/mage/cards/m/MelirasKeepers.java @@ -51,7 +51,7 @@ public class MelirasKeepers extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // Melira's Keepers can't have counters placed on it + // Melira's Keepers can't have counters put on it this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantHaveCountersSourceEffect())); } diff --git a/Mage.Sets/src/mage/cards/m/MoralityShift.java b/Mage.Sets/src/mage/cards/m/MoralityShift.java new file mode 100644 index 00000000000..23ab1dea3a7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoralityShift.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.cards.m; + +import java.util.List; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author jeffwadsworth + */ +public class MoralityShift extends CardImpl { + + public MoralityShift(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{B}{B}"); + + + // Exchange your graveyard and library. Then shuffle your library. + this.getSpellAbility().addEffect(new MoralityShiftEffect()); + + } + + public MoralityShift(final MoralityShift card) { + super(card); + } + + @Override + public MoralityShift copy() { + return new MoralityShift(this); + } +} + +class MoralityShiftEffect extends OneShotEffect { + + MoralityShiftEffect() { + super(Outcome.AIDontUseIt); + staticText = "Exchange your graveyard and library. Then shuffle your library."; + } + + MoralityShiftEffect(MoralityShiftEffect effect) { + super(effect); + } + + @Override + public MoralityShiftEffect copy() { + return new MoralityShiftEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + List copyLibrary = controller.getLibrary().getCards(game); + Set copyGraveyard = controller.getGraveyard().getCards(game); + controller.getLibrary().clear(); + controller.getGraveyard().clear(); + controller.getGraveyard().addAll(copyLibrary); + controller.getLibrary().addAll(copyGraveyard, game); + controller.shuffleLibrary(source, game); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MoxLotus.java b/Mage.Sets/src/mage/cards/m/MoxLotus.java new file mode 100644 index 00000000000..ced2a724b6d --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoxLotus.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.cards.m; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.AddManaOfAnyColorEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; + +/** + * + * @author spjspj/psjpsj + */ +public class MoxLotus extends CardImpl { + + public MoxLotus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{15}"); + + // {t}: Add infinity (or 1*10^9 to account for a potential mana reflection) to your mana pool. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(1000000000), new TapSourceCost())); + + // {100}: Add one mana of any color to your mana pool. + Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(1), new ManaCostsImpl("{100}")); + this.addAbility(ability); + + // You don't lose life due to mana burn. + // Situation normal?? + } + + public MoxLotus(final MoxLotus card) { + super(card); + } + + @Override + public MoxLotus copy() { + return new MoxLotus(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/NoMercy.java b/Mage.Sets/src/mage/cards/n/NoMercy.java index 59815c6adbd..7c4d0079f44 100644 --- a/Mage.Sets/src/mage/cards/n/NoMercy.java +++ b/Mage.Sets/src/mage/cards/n/NoMercy.java @@ -29,7 +29,6 @@ package mage.cards.n; import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -48,7 +47,7 @@ import mage.target.targetpointer.FixedTarget; public class NoMercy extends CardImpl { public NoMercy(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}"); // Whenever a creature deals damage to you, destroy it. this.addAbility(new NoMercyTriggeredAbility()); @@ -88,9 +87,7 @@ public class NoMercy extends CardImpl { if (event.getPlayerId().equals(this.getControllerId())) { Permanent permanent = game.getPermanent(event.getSourceId()); if (permanent != null && permanent.isCreature()) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getSourceId())); - } + this.getEffects().setTargetPointer(new FixedTarget(permanent, game)); return true; } } diff --git a/Mage.Sets/src/mage/cards/p/Pandemonium.java b/Mage.Sets/src/mage/cards/p/Pandemonium.java index 433002b154c..38802297f5b 100644 --- a/Mage.Sets/src/mage/cards/p/Pandemonium.java +++ b/Mage.Sets/src/mage/cards/p/Pandemonium.java @@ -107,11 +107,11 @@ class PandemoniumEffect extends OneShotEffect { if (enteringCreature != null) { Permanent targetPermanent = game.getPermanent(source.getTargets().getFirstTarget()); if (targetPermanent != null) { - targetPermanent.damage(enteringCreature.getPower().getValue(), source.getSourceId(), game, false, true); + targetPermanent.damage(enteringCreature.getPower().getValue(), enteringCreature.getId(), game, false, true); } else { Player targetPlayer = game.getPlayer(source.getTargets().getFirstTarget()); if (targetPlayer != null) { - targetPlayer.damage(enteringCreature.getPower().getValue(), source.getSourceId(), game, false, true); + targetPlayer.damage(enteringCreature.getPower().getValue(), enteringCreature.getId(), game, false, true); } } return true; diff --git a/Mage.Sets/src/mage/cards/p/PrimalVigor.java b/Mage.Sets/src/mage/cards/p/PrimalVigor.java index 2d13fc5f72b..c3864291885 100644 --- a/Mage.Sets/src/mage/cards/p/PrimalVigor.java +++ b/Mage.Sets/src/mage/cards/p/PrimalVigor.java @@ -52,7 +52,7 @@ public class PrimalVigor extends CardImpl { // If one or more tokens would be created, twice that many of those tokens are created instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PrimalVigorTokenEffect())); - // If one or more +1/+1 counters would be placed on a creature, twice that many +1/+1 counters are placed on that creature instead. + // If one or more +1/+1 counters would be put on a creature, twice that many +1/+1 counters are put on that creature instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PrimalVigorCounterEffect())); } @@ -110,7 +110,7 @@ class PrimalVigorCounterEffect extends ReplacementEffectImpl { PrimalVigorCounterEffect() { super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false); - staticText = "If one or more +1/+1 counters would be placed on a creature, twice that many +1/+1 counters are placed on that creature instead"; + staticText = "If one or more +1/+1 counters would be put on a creature, twice that many +1/+1 counters are put on that creature instead"; } PrimalVigorCounterEffect(final PrimalVigorCounterEffect effect) { diff --git a/Mage.Sets/src/mage/cards/q/Quagmire.java b/Mage.Sets/src/mage/cards/q/Quagmire.java new file mode 100644 index 00000000000..302e508b342 --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/Quagmire.java @@ -0,0 +1,91 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.q; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.AsThoughEffectType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; + +/** + * + * @author L_J + */ +public class Quagmire extends CardImpl { + + public Quagmire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + // Creatures with swampwalk can be blocked as though they didn't have swampwalk. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new QuagmireEffect())); + } + + public Quagmire(final Quagmire card) { + super(card); + } + + @Override + public Quagmire copy() { + return new Quagmire(this); + } +} + +class QuagmireEffect extends AsThoughEffectImpl { + + public QuagmireEffect() { + super(AsThoughEffectType.BLOCK_SWAMPWALK, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Creatures with swampwalk can be blocked as though they didn't have swampwalk"; + } + + public QuagmireEffect(final QuagmireEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public QuagmireEffect copy() { + return new QuagmireEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SkullmaneBaku.java b/Mage.Sets/src/mage/cards/s/SkullmaneBaku.java index 1e6e85191ad..3856de41a27 100644 --- a/Mage.Sets/src/mage/cards/s/SkullmaneBaku.java +++ b/Mage.Sets/src/mage/cards/s/SkullmaneBaku.java @@ -25,34 +25,34 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; /** * @author LevelX2 @@ -60,12 +60,12 @@ import mage.target.common.TargetCreaturePermanent; public class SkullmaneBaku extends CardImpl { public SkullmaneBaku(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.SPIRIT); this.power = new MageInt(2); this.toughness = new MageInt(1); - + // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Skullmane Baku. this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.SPIRIT_OR_ARCANE_CARD, true)); @@ -85,7 +85,7 @@ public class SkullmaneBaku extends CardImpl { public SkullmaneBaku copy() { return new SkullmaneBaku(this); } - + static class SkullmaneBakuUnboostEffect extends OneShotEffect { public SkullmaneBakuUnboostEffect() { @@ -102,12 +102,14 @@ public class SkullmaneBaku extends CardImpl { int numberToUnboost = 0; for (Cost cost : source.getCosts()) { if (cost instanceof RemoveVariableCountersSourceCost) { - numberToUnboost = ((RemoveVariableCountersSourceCost)cost).getAmount() * -1; + numberToUnboost = ((RemoveVariableCountersSourceCost) cost).getAmount() * -1; } } - Permanent creature = game.getPermanent(targetPointer.getFirst(game, source)); + Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); if (creature != null && numberToUnboost != 0) { - creature.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceEffect(numberToUnboost, numberToUnboost, Duration.EndOfTurn)), source.getSourceId(), game, false); + ContinuousEffect effect = new BoostTargetEffect(numberToUnboost, numberToUnboost, Duration.EndOfTurn); + effect.setTargetPointer(new FixedTarget(creature, game)); + game.addEffect(effect, source); } return true; } @@ -118,4 +120,4 @@ public class SkullmaneBaku extends CardImpl { } } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/StaffOfTheAges.java b/Mage.Sets/src/mage/cards/s/StaffOfTheAges.java new file mode 100644 index 00000000000..9d3e1fc25d7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StaffOfTheAges.java @@ -0,0 +1,91 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.s; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.AsThoughEffectType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; + +/** + * + * @author L_J + */ +public class StaffOfTheAges extends CardImpl { + + public StaffOfTheAges(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // Creatures with landwalk abilities can be blocked as though they didn't have those abilities. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new StaffOfTheAgesEffect())); + } + + public StaffOfTheAges(final StaffOfTheAges card) { + super(card); + } + + @Override + public StaffOfTheAges copy() { + return new StaffOfTheAges(this); + } +} + +class StaffOfTheAgesEffect extends AsThoughEffectImpl { + + public StaffOfTheAgesEffect() { + super(AsThoughEffectType.BLOCK_LANDWALK, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Creatures with landwalk abilities can be blocked as though they didn't have those abilities"; + } + + public StaffOfTheAgesEffect(final StaffOfTheAgesEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public StaffOfTheAgesEffect copy() { + return new StaffOfTheAgesEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/Tatterkite.java b/Mage.Sets/src/mage/cards/t/Tatterkite.java index e7997380f45..dadd2c8be09 100644 --- a/Mage.Sets/src/mage/cards/t/Tatterkite.java +++ b/Mage.Sets/src/mage/cards/t/Tatterkite.java @@ -53,7 +53,7 @@ public class Tatterkite extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // Tatterkite can't have counters placed on it. + // Tatterkite can't have counters put on it. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantHaveCountersSourceEffect())); } diff --git a/Mage.Sets/src/mage/cards/u/Undertow.java b/Mage.Sets/src/mage/cards/u/Undertow.java new file mode 100644 index 00000000000..d14bf5f4062 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/Undertow.java @@ -0,0 +1,91 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.u; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.AsThoughEffectType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; + +/** + * + * @author L_J + */ +public class Undertow extends CardImpl { + + public Undertow(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + // Creatures with islandwalk can be blocked as though they didn't have islandwalk. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UndertowEffect())); + } + + public Undertow(final Undertow card) { + super(card); + } + + @Override + public Undertow copy() { + return new Undertow(this); + } +} + +class UndertowEffect extends AsThoughEffectImpl { + + public UndertowEffect() { + super(AsThoughEffectType.BLOCK_ISLANDWALK, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Creatures with islandwalk can be blocked as though they didn't have islandwalk"; + } + + public UndertowEffect(final UndertowEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public UndertowEffect copy() { + return new UndertowEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/u/UphillBattle.java b/Mage.Sets/src/mage/cards/u/UphillBattle.java new file mode 100644 index 00000000000..86ed97b4728 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UphillBattle.java @@ -0,0 +1,168 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.u; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; +import mage.watchers.common.CreatureWasCastWatcher; + +/** + * + * @author chrvanorle + */ +public class UphillBattle extends CardImpl { + + public UphillBattle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + + // Creatures played by your opponents enter the battlefield tapped. + Ability tapAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, new UphillBattleTapEffect()); + tapAbility.addWatcher(new CreatureWasCastWatcher()); + tapAbility.addWatcher(new PlayCreatureLandWatcher()); + addAbility(tapAbility); + } + + public UphillBattle(final UphillBattle card) { + super(card); + } + + @Override + public UphillBattle copy() { + return new UphillBattle(this); + } +} + +class PlayCreatureLandWatcher extends Watcher { + + final Set playerPlayedLand = new HashSet<>(); // player that played land + final Set landPlayed = new HashSet<>(); // land played + + public PlayCreatureLandWatcher() { + super(PlayCreatureLandWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public PlayCreatureLandWatcher(final PlayCreatureLandWatcher watcher) { + super(watcher); + playerPlayedLand.addAll(watcher.playerPlayedLand); + landPlayed.addAll(watcher.landPlayed); + } + + @Override + public PlayCreatureLandWatcher copy() { + return new PlayCreatureLandWatcher(this); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.PLAY_LAND) { + Card card = game.getCard(event.getTargetId()); + if (card != null + && card.isLand() + && card.isCreature() + && !playerPlayedLand.contains(event.getPlayerId())) { + playerPlayedLand.add(event.getPlayerId()); + landPlayed.add(event.getTargetId()); + } + } + } + + @Override + public void reset() { + playerPlayedLand.clear(); + landPlayed.clear(); + super.reset(); + } + + public boolean landPlayed(UUID playerId) { + return playerPlayedLand.contains(playerId); + } + + public boolean wasLandPlayed(UUID landId) { + return landPlayed.contains(landId); + } +} + +class UphillBattleTapEffect extends ReplacementEffectImpl { + + UphillBattleTapEffect() { + super(Duration.WhileOnBattlefield, Outcome.Tap); + staticText = "Creatures played by your opponents enter the battlefield tapped"; + } + + UphillBattleTapEffect(final UphillBattleTapEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); + CreatureWasCastWatcher creatureSpellWatcher = (CreatureWasCastWatcher) game.getState().getWatchers().get(CreatureWasCastWatcher.class.getSimpleName()); + PlayCreatureLandWatcher landWatcher = (PlayCreatureLandWatcher) game.getState().getWatchers().get(PlayCreatureLandWatcher.class.getSimpleName()); + + if (target != null + && ((creatureSpellWatcher != null && creatureSpellWatcher.wasCreatureCastThisTurn(target.getId())) + || (landWatcher != null && landWatcher.wasLandPlayed(target.getId())))) { + target.setTapped(true); + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { + Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); + if (permanent != null && permanent.isCreature()) { + return true; + } + } + return false; + } + + @Override + public UphillBattleTapEffect copy() { + return new UphillBattleTapEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UrDrago.java b/Mage.Sets/src/mage/cards/u/UrDrago.java new file mode 100644 index 00000000000..42fbfc35053 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UrDrago.java @@ -0,0 +1,98 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.u; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author L_J + */ +public class UrDrago extends CardImpl { + + public UrDrago(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}{B}{B}"); + addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Creatures with swampwalk can be blocked as though they didn't have swampwalk. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new UrDragoEffect())); + } + + public UrDrago(final UrDrago card) { + super(card); + } + + @Override + public UrDrago copy() { + return new UrDrago(this); + } +} + +class UrDragoEffect extends AsThoughEffectImpl { + + public UrDragoEffect() { + super(AsThoughEffectType.BLOCK_SWAMPWALK, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Creatures with swampwalk can be blocked as though they didn't have swampwalk"; + } + + public UrDragoEffect(final UrDragoEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public UrDragoEffect copy() { + return new UrDragoEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WhipOfErebos.java b/Mage.Sets/src/mage/cards/w/WhipOfErebos.java index 8f7562fd233..33d3c1f0704 100644 --- a/Mage.Sets/src/mage/cards/w/WhipOfErebos.java +++ b/Mage.Sets/src/mage/cards/w/WhipOfErebos.java @@ -148,12 +148,8 @@ class WhipOfErebosReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - Player controller = game.getPlayer(source.getControllerId()); - if (permanent != null && controller != null) { - controller.moveCardToExileWithInfo(permanent, null, null, source.getSourceId(), game, Zone.BATTLEFIELD, true); - } - return true; + ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + return false; } @Override diff --git a/Mage.Sets/src/mage/cards/w/WindingConstrictor.java b/Mage.Sets/src/mage/cards/w/WindingConstrictor.java index 1b49a03c04c..01d253005ab 100644 --- a/Mage.Sets/src/mage/cards/w/WindingConstrictor.java +++ b/Mage.Sets/src/mage/cards/w/WindingConstrictor.java @@ -57,8 +57,8 @@ public class WindingConstrictor extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(3); - // If one or more counters would be placed on an artifact or creature you control, that many plus one of each of those kinds of counters are placed on that permanent instead. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new WindingConstrictorPermanentEffect())); + // If one or more counters would be put on an artifact or creature you control, that many plus one of each of those kinds of counters are put on that permanent instead. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new WindingConstrictorPermanentEffect())); // If you would get one or more counters, you get that many plus one of each of those kinds of counters instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new WindingConstrictorPlayerEffect())); @@ -78,8 +78,8 @@ class WindingConstrictorPermanentEffect extends ReplacementEffectImpl { WindingConstrictorPermanentEffect() { super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false); - staticText = "If one or more counters would be placed on an artifact or creature you control, " - + "that many plus one of each of those kinds of counters are placed on that permanent instead"; + staticText = "If one or more counters would be put on an artifact or creature you control, " + + "that many plus one of each of those kinds of counters are put on that permanent instead"; } WindingConstrictorPermanentEffect(final WindingConstrictorPermanentEffect effect) { diff --git a/Mage.Sets/src/mage/sets/Alliances.java b/Mage.Sets/src/mage/sets/Alliances.java index 9bb4de97f42..edc51da164f 100644 --- a/Mage.Sets/src/mage/sets/Alliances.java +++ b/Mage.Sets/src/mage/sets/Alliances.java @@ -40,165 +40,165 @@ public class Alliances extends ExpansionSet { this.numBoosterUncommon = 3; this.numBoosterRare = 1; this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Aesthir Glider", 156, Rarity.COMMON, AesthirGlider.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Aesthir Glider", 157, Rarity.COMMON, AesthirGlider.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Agent of Stromgald", 94, Rarity.COMMON, AgentOfStromgald.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Agent of Stromgald", 95, Rarity.COMMON, AgentOfStromgald.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Arcane Denial", 32, Rarity.COMMON, mage.cards.a.ArcaneDenial.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Arcane Denial", 33, Rarity.COMMON, mage.cards.a.ArcaneDenial.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Ashnod's Cylix", 158, Rarity.RARE, mage.cards.a.AshnodsCylix.class)); - cards.add(new SetCardInfo("Astrolabe", 159, Rarity.COMMON, mage.cards.a.Astrolabe.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Astrolabe", 160, Rarity.COMMON, mage.cards.a.Astrolabe.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Balduvian Dead", 1, Rarity.UNCOMMON, mage.cards.b.BalduvianDead.class)); - cards.add(new SetCardInfo("Balduvian Horde", 96, Rarity.RARE, mage.cards.b.BalduvianHorde.class)); - cards.add(new SetCardInfo("Balduvian Trading Post", 182, Rarity.RARE, mage.cards.b.BalduvianTradingPost.class)); - cards.add(new SetCardInfo("Balduvian War-Makers", 97, Rarity.COMMON, BalduvianWarMakers.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Balduvian War-Makers", 98, Rarity.COMMON, BalduvianWarMakers.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Bounty of the Hunt", 63, Rarity.UNCOMMON, mage.cards.b.BountyOfTheHunt.class)); - cards.add(new SetCardInfo("Browse", 38, Rarity.UNCOMMON, mage.cards.b.Browse.class)); - cards.add(new SetCardInfo("Burnout", 101, Rarity.UNCOMMON, mage.cards.b.Burnout.class)); - cards.add(new SetCardInfo("Carrier Pigeons", 125, Rarity.COMMON, CarrierPigeons.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Carrier Pigeons", 126, Rarity.COMMON, CarrierPigeons.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Chaos Harlequin", 102, Rarity.RARE, mage.cards.c.ChaosHarlequin.class)); - cards.add(new SetCardInfo("Contagion", 4, Rarity.UNCOMMON, mage.cards.c.Contagion.class)); - cards.add(new SetCardInfo("Deadly Insect", 64, Rarity.COMMON, mage.cards.d.DeadlyInsect.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Deadly Insect", 65, Rarity.COMMON, mage.cards.d.DeadlyInsect.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Death Spark", 103, Rarity.UNCOMMON, mage.cards.d.DeathSpark.class)); - cards.add(new SetCardInfo("Diminishing Returns", 39, Rarity.RARE, mage.cards.d.DiminishingReturns.class)); - cards.add(new SetCardInfo("Dystopia", 6, Rarity.RARE, mage.cards.d.Dystopia.class)); - cards.add(new SetCardInfo("Elvish Bard", 66, Rarity.UNCOMMON, mage.cards.e.ElvishBard.class)); - cards.add(new SetCardInfo("Elvish Ranger", 67, Rarity.COMMON, mage.cards.e.ElvishRanger.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Elvish Ranger", 68, Rarity.COMMON, mage.cards.e.ElvishRanger.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Elvish Spirit Guide", 69, Rarity.UNCOMMON, mage.cards.e.ElvishSpiritGuide.class)); - cards.add(new SetCardInfo("Energy Arc", 190, Rarity.UNCOMMON, mage.cards.e.EnergyArc.class)); - cards.add(new SetCardInfo("Enslaved Scout", 104, Rarity.COMMON, EnslavedScout.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Enslaved Scout", 105, Rarity.COMMON, EnslavedScout.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Errand of Duty", 127, Rarity.COMMON, mage.cards.e.ErrandOfDuty.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Errand of Duty", 128, Rarity.COMMON, mage.cards.e.ErrandOfDuty.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Exile", 129, Rarity.RARE, mage.cards.e.Exile.class)); - cards.add(new SetCardInfo("False Demise", 40, Rarity.COMMON, mage.cards.f.FalseDemise.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("False Demise", 41, Rarity.COMMON, mage.cards.f.FalseDemise.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Feast or Famine", 8, Rarity.COMMON, mage.cards.f.FeastOrFamine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Feast or Famine", 9, Rarity.COMMON, mage.cards.f.FeastOrFamine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fevered Strength", 10, Rarity.COMMON, FeveredStrength.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fevered Strength", 11, Rarity.COMMON, FeveredStrength.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Floodwater Dam", 161, Rarity.RARE, mage.cards.f.FloodwaterDam.class)); - cards.add(new SetCardInfo("Force of Will", 42, Rarity.UNCOMMON, mage.cards.f.ForceOfWill.class)); - cards.add(new SetCardInfo("Foresight", 43, Rarity.COMMON, mage.cards.f.Foresight.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Foresight", 44, Rarity.COMMON, mage.cards.f.Foresight.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla Berserkers", 75, Rarity.COMMON, GorillaBerserkers.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla Berserkers", 76, Rarity.COMMON, GorillaBerserkers.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla Chieftain", 77, Rarity.COMMON, mage.cards.g.GorillaChieftain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla Chieftain", 78, Rarity.COMMON, mage.cards.g.GorillaChieftain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla Shaman", 106, Rarity.COMMON, GorillaShaman.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla Shaman", 107, Rarity.UNCOMMON, GorillaShaman.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla War Cry", 108, Rarity.COMMON, mage.cards.g.GorillaWarCry.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gorilla War Cry", 109, Rarity.COMMON, mage.cards.g.GorillaWarCry.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Guerrilla Tactics", 110, Rarity.COMMON, mage.cards.g.GuerrillaTactics.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Guerrilla Tactics", 111, Rarity.COMMON, mage.cards.g.GuerrillaTactics.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Hail Storm", 79, Rarity.UNCOMMON, mage.cards.h.HailStorm.class)); - cards.add(new SetCardInfo("Heart of Yavimaya", 183, Rarity.RARE, mage.cards.h.HeartOfYavimaya.class)); - cards.add(new SetCardInfo("Helm of Obedience", 163, Rarity.RARE, mage.cards.h.HelmOfObedience.class)); - cards.add(new SetCardInfo("Inheritance", 130, Rarity.UNCOMMON, mage.cards.i.Inheritance.class)); - cards.add(new SetCardInfo("Insidious Bookworms", 12, Rarity.COMMON, InsidiousBookworms.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Insidious Bookworms", 13, Rarity.COMMON, InsidiousBookworms.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Juniper Order Advocate", 132, Rarity.UNCOMMON, mage.cards.j.JuniperOrderAdvocate.class)); - cards.add(new SetCardInfo("Kaysa", 80, Rarity.RARE, mage.cards.k.Kaysa.class)); - cards.add(new SetCardInfo("Keeper of Tresserhorn", 14, Rarity.RARE, mage.cards.k.KeeperOfTresserhorn.class)); - cards.add(new SetCardInfo("Kjeldoran Escort", 133, Rarity.COMMON, mage.cards.k.KjeldoranEscort.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kjeldoran Escort", 134, Rarity.COMMON, mage.cards.k.KjeldoranEscort.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kjeldoran Home Guard", 135, Rarity.UNCOMMON, mage.cards.k.KjeldoranHomeGuard.class)); - cards.add(new SetCardInfo("Kjeldoran Outpost", 184, Rarity.RARE, mage.cards.k.KjeldoranOutpost.class)); - cards.add(new SetCardInfo("Krovikan Horror", 15, Rarity.RARE, mage.cards.k.KrovikanHorror.class)); - cards.add(new SetCardInfo("Krovikan Plague", 16, Rarity.UNCOMMON, mage.cards.k.KrovikanPlague.class)); - cards.add(new SetCardInfo("Lake of the Dead", 185, Rarity.RARE, mage.cards.l.LakeOfTheDead.class)); - cards.add(new SetCardInfo("Library of Lat-Nam", 47, Rarity.RARE, mage.cards.l.LibraryOfLatNam.class)); - cards.add(new SetCardInfo("Lim-Dul's High Guard", 17, Rarity.COMMON, LimDulsHighGuard.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Lim-Dul's High Guard", 18, Rarity.COMMON, LimDulsHighGuard.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Lim-Dul's Paladin", 191, Rarity.UNCOMMON, mage.cards.l.LimDulsPaladin.class)); - cards.add(new SetCardInfo("Lim-Dul's Vault", 192, Rarity.UNCOMMON, mage.cards.l.LimDulsVault.class)); - cards.add(new SetCardInfo("Lodestone Bauble", 164, Rarity.RARE, mage.cards.l.LodestoneBauble.class)); - cards.add(new SetCardInfo("Lord of Tresserhorn", 193, Rarity.RARE, mage.cards.l.LordOfTresserhorn.class)); - cards.add(new SetCardInfo("Martyrdom", 138, Rarity.COMMON, mage.cards.m.Martyrdom.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Martyrdom", 139, Rarity.COMMON, mage.cards.m.Martyrdom.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mishra's Groundbreaker", 165, Rarity.UNCOMMON, mage.cards.m.MishrasGroundbreaker.class)); - cards.add(new SetCardInfo("Misinformation", 19, Rarity.UNCOMMON, mage.cards.m.Misinformation.class)); - cards.add(new SetCardInfo("Mystic Compass", 166, Rarity.UNCOMMON, mage.cards.m.MysticCompass.class)); - cards.add(new SetCardInfo("Nature's Blessing", 195, Rarity.UNCOMMON, mage.cards.n.NaturesBlessing.class)); - cards.add(new SetCardInfo("Nature's Chosen", 81, Rarity.UNCOMMON, mage.cards.n.NaturesChosen.class)); - cards.add(new SetCardInfo("Nature's Wrath", 82, Rarity.RARE, mage.cards.n.NaturesWrath.class)); - cards.add(new SetCardInfo("Noble Steeds", 140, Rarity.COMMON, mage.cards.n.NobleSteeds.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Noble Steeds", 141, Rarity.COMMON, mage.cards.n.NobleSteeds.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Omen of Fire", 112, Rarity.RARE, mage.cards.o.OmenOfFire.class)); - cards.add(new SetCardInfo("Phantasmal Fiend", 20, Rarity.COMMON, mage.cards.p.PhantasmalFiend.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Phantasmal Fiend", 21, Rarity.COMMON, mage.cards.p.PhantasmalFiend.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Phelddagrif", 196, Rarity.RARE, mage.cards.p.Phelddagrif.class)); - cards.add(new SetCardInfo("Phyrexian Boon", 22, Rarity.COMMON, mage.cards.p.PhyrexianBoon.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Phyrexian Boon", 23, Rarity.COMMON, mage.cards.p.PhyrexianBoon.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Phyrexian Devourer", 167, Rarity.RARE, mage.cards.p.PhyrexianDevourer.class)); - cards.add(new SetCardInfo("Phyrexian War Beast", 169, Rarity.COMMON, PhyrexianWarBeast.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Phyrexian War Beast", 170, Rarity.COMMON, PhyrexianWarBeast.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Pillage", 113, Rarity.UNCOMMON, mage.cards.p.Pillage.class)); - cards.add(new SetCardInfo("Pyrokinesis", 115, Rarity.UNCOMMON, mage.cards.p.Pyrokinesis.class)); - cards.add(new SetCardInfo("Reinforcements", 142, Rarity.COMMON, mage.cards.r.Reinforcements.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Reinforcements", 143, Rarity.COMMON, mage.cards.r.Reinforcements.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Reprisal", 144, Rarity.COMMON, mage.cards.r.Reprisal.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Reprisal", 145, Rarity.COMMON, mage.cards.r.Reprisal.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Ritual of the Machine", 24, Rarity.RARE, mage.cards.r.RitualOfTheMachine.class)); - cards.add(new SetCardInfo("Royal Decree", 146, Rarity.RARE, mage.cards.r.RoyalDecree.class)); - cards.add(new SetCardInfo("Royal Herbalist", 147, Rarity.COMMON, RoyalHerbalist.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Royal Herbalist", 148, Rarity.COMMON, RoyalHerbalist.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("School of the Unseen", 186, Rarity.UNCOMMON, mage.cards.s.SchoolOfTheUnseen.class)); - cards.add(new SetCardInfo("Seasoned Tactician", 150, Rarity.UNCOMMON, mage.cards.s.SeasonedTactician.class)); - cards.add(new SetCardInfo("Sheltered Valley", 187, Rarity.RARE, mage.cards.s.ShelteredValley.class)); - cards.add(new SetCardInfo("Shield Sphere", 172, Rarity.UNCOMMON, mage.cards.s.ShieldSphere.class)); - cards.add(new SetCardInfo("Soldevi Adnate", 25, Rarity.COMMON, SoldeviAdnate.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Adnate", 26, Rarity.COMMON, SoldeviAdnate.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Digger", 174, Rarity.RARE, mage.cards.s.SoldeviDigger.class)); - cards.add(new SetCardInfo("Soldevi Excavations", 188, Rarity.RARE, mage.cards.s.SoldeviExcavations.class)); - cards.add(new SetCardInfo("Soldevi Heretic", 49, Rarity.COMMON, mage.cards.s.SoldeviHeretic.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Heretic", 50, Rarity.COMMON, mage.cards.s.SoldeviHeretic.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Sage", 51, Rarity.COMMON, SoldeviSage.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Sage", 52, Rarity.COMMON, SoldeviSage.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Steam Beast", 177, Rarity.COMMON, mage.cards.s.SoldeviSteamBeast.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldevi Steam Beast", 178, Rarity.COMMON, mage.cards.s.SoldeviSteamBeast.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soldier of Fortune", 117, Rarity.UNCOMMON, mage.cards.s.SoldierOfFortune.class)); - cards.add(new SetCardInfo("Sol Grail", 173, Rarity.UNCOMMON, mage.cards.s.SolGrail.class)); - cards.add(new SetCardInfo("Stench of Decay", 27, Rarity.COMMON, mage.cards.s.StenchOfDecay.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Stench of Decay", 28, Rarity.COMMON, mage.cards.s.StenchOfDecay.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Storm Cauldron", 179, Rarity.RARE, mage.cards.s.StormCauldron.class)); - cards.add(new SetCardInfo("Storm Crow", 54, Rarity.COMMON, mage.cards.s.StormCrow.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Storm Crow", 55, Rarity.COMMON, mage.cards.s.StormCrow.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Storm Shaman", 118, Rarity.COMMON, StormShaman.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Storm Shaman", 119, Rarity.UNCOMMON, StormShaman.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Stromgald Spy", 29, Rarity.UNCOMMON, mage.cards.s.StromgaldSpy.class)); - cards.add(new SetCardInfo("Surge of Strength", 197, Rarity.UNCOMMON, mage.cards.s.SurgeOfStrength.class)); - cards.add(new SetCardInfo("Sustaining Spirit", 151, Rarity.RARE, mage.cards.s.SustainingSpirit.class)); - cards.add(new SetCardInfo("Swamp Mosquito", 30, Rarity.COMMON, SwampMosquito.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp Mosquito", 31, Rarity.COMMON, SwampMosquito.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Thawing Glaciers", 189, Rarity.RARE, mage.cards.t.ThawingGlaciers.class)); - cards.add(new SetCardInfo("Thought Lash", 58, Rarity.RARE, mage.cards.t.ThoughtLash.class)); - cards.add(new SetCardInfo("Tidal Control", 59, Rarity.RARE, mage.cards.t.TidalControl.class)); - cards.add(new SetCardInfo("Tornado", 86, Rarity.RARE, mage.cards.t.Tornado.class)); - cards.add(new SetCardInfo("Unlikely Alliance", 153, Rarity.UNCOMMON, mage.cards.u.UnlikelyAlliance.class)); - cards.add(new SetCardInfo("Urza's Engine", 180, Rarity.UNCOMMON, mage.cards.u.UrzasEngine.class)); - cards.add(new SetCardInfo("Varchild's Crusader", 120, Rarity.COMMON, mage.cards.v.VarchildsCrusader.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Varchild's Crusader", 121, Rarity.COMMON, mage.cards.v.VarchildsCrusader.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Varchild's War-Riders", 122, Rarity.RARE, mage.cards.v.VarchildsWarRiders.class)); - cards.add(new SetCardInfo("Veteran's Voice", 123, Rarity.COMMON, mage.cards.v.VeteransVoice.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Veteran's Voice", 124, Rarity.COMMON, mage.cards.v.VeteransVoice.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Viscerid Armor", 60, Rarity.COMMON, mage.cards.v.VisceridArmor.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Viscerid Armor", 61, Rarity.COMMON, mage.cards.v.VisceridArmor.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Viscerid Drone", 62, Rarity.UNCOMMON, mage.cards.v.VisceridDrone.class)); - cards.add(new SetCardInfo("Wandering Mage", 198, Rarity.RARE, mage.cards.w.WanderingMage.class)); - cards.add(new SetCardInfo("Whip Vine", 89, Rarity.COMMON, WhipVine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Whip Vine", 90, Rarity.COMMON, WhipVine.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Whirling Catapult", 181, Rarity.UNCOMMON, mage.cards.w.WhirlingCatapult.class)); - cards.add(new SetCardInfo("Wild Aesthir", 154, Rarity.COMMON, mage.cards.w.WildAesthir.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Wild Aesthir", 155, Rarity.COMMON, mage.cards.w.WildAesthir.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Winter's Night", 199, Rarity.RARE, mage.cards.w.WintersNight.class)); - cards.add(new SetCardInfo("Yavimaya Ancients", 91, Rarity.COMMON, mage.cards.y.YavimayaAncients.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Yavimaya Ancients", 92, Rarity.COMMON, mage.cards.y.YavimayaAncients.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Yavimaya Ants", 93, Rarity.UNCOMMON, mage.cards.y.YavimayaAnts.class)); + cards.add(new SetCardInfo("Aesthir Glider", "116a", Rarity.COMMON, mage.cards.a.AesthirGlider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aesthir Glider", "116b", Rarity.COMMON, mage.cards.a.AesthirGlider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Agent of Stromgald", "64a", Rarity.COMMON, mage.cards.a.AgentOfStromgald.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Agent of Stromgald", "64b", Rarity.COMMON, mage.cards.a.AgentOfStromgald.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arcane Denial", "22a", Rarity.COMMON, mage.cards.a.ArcaneDenial.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arcane Denial", "22b", Rarity.COMMON, mage.cards.a.ArcaneDenial.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ashnod's Cylix", 117, Rarity.RARE, mage.cards.a.AshnodsCylix.class)); + cards.add(new SetCardInfo("Astrolabe", "118a", Rarity.COMMON, mage.cards.a.Astrolabe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Astrolabe", "118b", Rarity.COMMON, mage.cards.a.Astrolabe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balduvian Dead", 43, Rarity.UNCOMMON, mage.cards.b.BalduvianDead.class)); + cards.add(new SetCardInfo("Balduvian Horde", 65, Rarity.RARE, mage.cards.b.BalduvianHorde.class)); + cards.add(new SetCardInfo("Balduvian Trading Post", 137, Rarity.RARE, mage.cards.b.BalduvianTradingPost.class)); + cards.add(new SetCardInfo("Balduvian War-Makers", "66a", Rarity.COMMON, mage.cards.b.BalduvianWarMakers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balduvian War-Makers", "66b", Rarity.COMMON, mage.cards.b.BalduvianWarMakers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bounty of the Hunt", 85, Rarity.UNCOMMON, mage.cards.b.BountyOfTheHunt.class)); + cards.add(new SetCardInfo("Browse", 25, Rarity.UNCOMMON, mage.cards.b.Browse.class)); + cards.add(new SetCardInfo("Burnout", 68, Rarity.UNCOMMON, mage.cards.b.Burnout.class)); + cards.add(new SetCardInfo("Carrier Pigeons", "1a", Rarity.COMMON, mage.cards.c.CarrierPigeons.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Carrier Pigeons", "1b", Rarity.COMMON, mage.cards.c.CarrierPigeons.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chaos Harlequin", 69, Rarity.RARE, mage.cards.c.ChaosHarlequin.class)); + cards.add(new SetCardInfo("Contagion", 45, Rarity.UNCOMMON, mage.cards.c.Contagion.class)); + cards.add(new SetCardInfo("Deadly Insect", "86a", Rarity.COMMON, mage.cards.d.DeadlyInsect.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Deadly Insect", "86b", Rarity.COMMON, mage.cards.d.DeadlyInsect.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Death Spark", 70, Rarity.UNCOMMON, mage.cards.d.DeathSpark.class)); + cards.add(new SetCardInfo("Diminishing Returns", 26, Rarity.RARE, mage.cards.d.DiminishingReturns.class)); + cards.add(new SetCardInfo("Dystopia", 47, Rarity.RARE, mage.cards.d.Dystopia.class)); + cards.add(new SetCardInfo("Elvish Bard", 87, Rarity.UNCOMMON, mage.cards.e.ElvishBard.class)); + cards.add(new SetCardInfo("Elvish Ranger", "88a", Rarity.COMMON, mage.cards.e.ElvishRanger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elvish Ranger", "88b", Rarity.COMMON, mage.cards.e.ElvishRanger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Elvish Spirit Guide", 89, Rarity.UNCOMMON, mage.cards.e.ElvishSpiritGuide.class)); + cards.add(new SetCardInfo("Energy Arc", 106, Rarity.UNCOMMON, mage.cards.e.EnergyArc.class)); + cards.add(new SetCardInfo("Enslaved Scout", "71a", Rarity.COMMON, mage.cards.e.EnslavedScout.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Enslaved Scout", "71b", Rarity.COMMON, mage.cards.e.EnslavedScout.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Errand of Duty", "2a", Rarity.COMMON, mage.cards.e.ErrandOfDuty.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Errand of Duty", "2b", Rarity.COMMON, mage.cards.e.ErrandOfDuty.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Exile", 3, Rarity.RARE, mage.cards.e.Exile.class)); + cards.add(new SetCardInfo("False Demise", "27a", Rarity.COMMON, mage.cards.f.FalseDemise.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("False Demise", "27b", Rarity.COMMON, mage.cards.f.FalseDemise.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Feast or Famine", "49a", Rarity.COMMON, mage.cards.f.FeastOrFamine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Feast or Famine", "49b", Rarity.COMMON, mage.cards.f.FeastOrFamine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fevered Strength", "50a", Rarity.COMMON, mage.cards.f.FeveredStrength.class)); + cards.add(new SetCardInfo("Fevered Strength", "50b", Rarity.COMMON, mage.cards.f.FeveredStrength.class)); + cards.add(new SetCardInfo("Floodwater Dam", 119, Rarity.RARE, mage.cards.f.FloodwaterDam.class)); + cards.add(new SetCardInfo("Force of Will", 28, Rarity.UNCOMMON, mage.cards.f.ForceOfWill.class)); + cards.add(new SetCardInfo("Foresight", "29a", Rarity.COMMON, mage.cards.f.Foresight.class)); + cards.add(new SetCardInfo("Foresight", "29b", Rarity.COMMON, mage.cards.f.Foresight.class)); + cards.add(new SetCardInfo("Gorilla Berserkers", "93a", Rarity.COMMON, mage.cards.g.GorillaBerserkers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla Berserkers", "93b", Rarity.COMMON, mage.cards.g.GorillaBerserkers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla Chieftain", "94a", Rarity.COMMON, mage.cards.g.GorillaChieftain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla Chieftain", "94b", Rarity.COMMON, mage.cards.g.GorillaChieftain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla Shaman", "72a", Rarity.COMMON, mage.cards.g.GorillaShaman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla Shaman", "72b", Rarity.COMMON, mage.cards.g.GorillaShaman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla War Cry", "73a", Rarity.COMMON, mage.cards.g.GorillaWarCry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gorilla War Cry", "73b", Rarity.COMMON, mage.cards.g.GorillaWarCry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Guerrilla Tactics", "74a", Rarity.COMMON, mage.cards.g.GuerrillaTactics.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Guerrilla Tactics", "74b", Rarity.COMMON, mage.cards.g.GuerrillaTactics.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hail Storm", 95, Rarity.UNCOMMON, mage.cards.h.HailStorm.class)); + cards.add(new SetCardInfo("Heart of Yavimaya", 138, Rarity.RARE, mage.cards.h.HeartOfYavimaya.class)); + cards.add(new SetCardInfo("Helm of Obedience", 121, Rarity.RARE, mage.cards.h.HelmOfObedience.class)); + cards.add(new SetCardInfo("Inheritance", 4, Rarity.UNCOMMON, mage.cards.i.Inheritance.class)); + cards.add(new SetCardInfo("Insidious Bookworms", "51a", Rarity.COMMON, mage.cards.i.InsidiousBookworms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Insidious Bookworms", "51b", Rarity.COMMON, mage.cards.i.InsidiousBookworms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Juniper Order Advocate", 6, Rarity.UNCOMMON, mage.cards.j.JuniperOrderAdvocate.class)); + cards.add(new SetCardInfo("Kaysa", 96, Rarity.RARE, mage.cards.k.Kaysa.class)); + cards.add(new SetCardInfo("Keeper of Tresserhorn", 52, Rarity.RARE, mage.cards.k.KeeperOfTresserhorn.class)); + cards.add(new SetCardInfo("Kjeldoran Escort", "7a", Rarity.COMMON, mage.cards.k.KjeldoranEscort.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kjeldoran Escort", "7b", Rarity.COMMON, mage.cards.k.KjeldoranEscort.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kjeldoran Home Guard", 8, Rarity.UNCOMMON, mage.cards.k.KjeldoranHomeGuard.class)); + cards.add(new SetCardInfo("Kjeldoran Outpost", 139, Rarity.RARE, mage.cards.k.KjeldoranOutpost.class)); + cards.add(new SetCardInfo("Krovikan Horror", 53, Rarity.RARE, mage.cards.k.KrovikanHorror.class)); + cards.add(new SetCardInfo("Krovikan Plague", 54, Rarity.UNCOMMON, mage.cards.k.KrovikanPlague.class)); + cards.add(new SetCardInfo("Lake of the Dead", 140, Rarity.RARE, mage.cards.l.LakeOfTheDead.class)); + cards.add(new SetCardInfo("Library of Lat-Nam", 31, Rarity.RARE, mage.cards.l.LibraryOfLatNam.class)); + cards.add(new SetCardInfo("Lim-Dul's High Guard", "55a", Rarity.COMMON, mage.cards.l.LimDulsHighGuard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lim-Dul's High Guard", "55b", Rarity.COMMON, mage.cards.l.LimDulsHighGuard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lim-Dul's Paladin", 108, Rarity.UNCOMMON, mage.cards.l.LimDulsPaladin.class)); + cards.add(new SetCardInfo("Lim-Dul's Vault", 107, Rarity.UNCOMMON, mage.cards.l.LimDulsVault.class)); + cards.add(new SetCardInfo("Lodestone Bauble", 122, Rarity.RARE, mage.cards.l.LodestoneBauble.class)); + cards.add(new SetCardInfo("Lord of Tresserhorn", 112, Rarity.RARE, mage.cards.l.LordOfTresserhorn.class)); + cards.add(new SetCardInfo("Martyrdom", "10a", Rarity.COMMON, mage.cards.m.Martyrdom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Martyrdom", "10b", Rarity.COMMON, mage.cards.m.Martyrdom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mishra's Groundbreaker", 123, Rarity.UNCOMMON, mage.cards.m.MishrasGroundbreaker.class)); + cards.add(new SetCardInfo("Misinformation", 56, Rarity.UNCOMMON, mage.cards.m.Misinformation.class)); + cards.add(new SetCardInfo("Mystic Compass", 124, Rarity.UNCOMMON, mage.cards.m.MysticCompass.class)); + cards.add(new SetCardInfo("Nature's Blessing", 110, Rarity.UNCOMMON, mage.cards.n.NaturesBlessing.class)); + cards.add(new SetCardInfo("Nature's Chosen", 97, Rarity.UNCOMMON, mage.cards.n.NaturesChosen.class)); + cards.add(new SetCardInfo("Nature's Wrath", 98, Rarity.RARE, mage.cards.n.NaturesWrath.class)); + cards.add(new SetCardInfo("Noble Steeds", "11a", Rarity.COMMON, mage.cards.n.NobleSteeds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Noble Steeds", "11b", Rarity.COMMON, mage.cards.n.NobleSteeds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Omen of Fire", 75, Rarity.RARE, mage.cards.o.OmenOfFire.class)); + cards.add(new SetCardInfo("Phantasmal Fiend", "57a", Rarity.COMMON, mage.cards.p.PhantasmalFiend.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phantasmal Fiend", "57b", Rarity.COMMON, mage.cards.p.PhantasmalFiend.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phelddagrif", 115, Rarity.RARE, mage.cards.p.Phelddagrif.class)); + cards.add(new SetCardInfo("Phyrexian Boon", "58a", Rarity.COMMON, mage.cards.p.PhyrexianBoon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phyrexian Boon", "58b", Rarity.COMMON, mage.cards.p.PhyrexianBoon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phyrexian Devourer", 125, Rarity.RARE, mage.cards.p.PhyrexianDevourer.class)); + cards.add(new SetCardInfo("Phyrexian War Beast", "127a", Rarity.COMMON, mage.cards.p.PhyrexianWarBeast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phyrexian War Beast", "127b", Rarity.COMMON, mage.cards.p.PhyrexianWarBeast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pillage", 76, Rarity.UNCOMMON, mage.cards.p.Pillage.class)); + cards.add(new SetCardInfo("Pyrokinesis", 78, Rarity.UNCOMMON, mage.cards.p.Pyrokinesis.class)); + cards.add(new SetCardInfo("Reinforcements", "12a", Rarity.COMMON, mage.cards.r.Reinforcements.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reinforcements", "12b", Rarity.COMMON, mage.cards.r.Reinforcements.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reprisal", "13a", Rarity.COMMON, mage.cards.r.Reprisal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reprisal", "13b", Rarity.COMMON, mage.cards.r.Reprisal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ritual of the Machine", 59, Rarity.RARE, mage.cards.r.RitualOfTheMachine.class)); + cards.add(new SetCardInfo("Royal Decree", 14, Rarity.RARE, mage.cards.r.RoyalDecree.class)); + cards.add(new SetCardInfo("Royal Herbalist", "15a", Rarity.COMMON, mage.cards.r.RoyalHerbalist.class)); + cards.add(new SetCardInfo("Royal Herbalist", "15b", Rarity.COMMON, mage.cards.r.RoyalHerbalist.class)); + cards.add(new SetCardInfo("School of the Unseen", 141, Rarity.UNCOMMON, mage.cards.s.SchoolOfTheUnseen.class)); + cards.add(new SetCardInfo("Seasoned Tactician", 17, Rarity.UNCOMMON, mage.cards.s.SeasonedTactician.class)); + cards.add(new SetCardInfo("Sheltered Valley", 142, Rarity.RARE, mage.cards.s.ShelteredValley.class)); + cards.add(new SetCardInfo("Shield Sphere", 129, Rarity.UNCOMMON, mage.cards.s.ShieldSphere.class)); + cards.add(new SetCardInfo("Sol Grail", 130, Rarity.UNCOMMON, mage.cards.s.SolGrail.class)); + cards.add(new SetCardInfo("Soldevi Adnate", "60a", Rarity.COMMON, mage.cards.s.SoldeviAdnate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Adnate", "60b", Rarity.COMMON, mage.cards.s.SoldeviAdnate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Digger", 131, Rarity.RARE, mage.cards.s.SoldeviDigger.class)); + cards.add(new SetCardInfo("Soldevi Excavations", 143, Rarity.RARE, mage.cards.s.SoldeviExcavations.class)); + cards.add(new SetCardInfo("Soldevi Heretic", "33a", Rarity.COMMON, mage.cards.s.SoldeviHeretic.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Heretic", "33b", Rarity.COMMON, mage.cards.s.SoldeviHeretic.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Sage", "34a", Rarity.COMMON, mage.cards.s.SoldeviSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Sage", "34b", Rarity.COMMON, mage.cards.s.SoldeviSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Steam Beast", "133a", Rarity.COMMON, mage.cards.s.SoldeviSteamBeast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldevi Steam Beast", "133b", Rarity.COMMON, mage.cards.s.SoldeviSteamBeast.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soldier of Fortune", 80, Rarity.UNCOMMON, mage.cards.s.SoldierOfFortune.class)); + cards.add(new SetCardInfo("Stench of Decay", "61a", Rarity.COMMON, mage.cards.s.StenchOfDecay.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stench of Decay", "61b", Rarity.COMMON, mage.cards.s.StenchOfDecay.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm Cauldron", 134, Rarity.RARE, mage.cards.s.StormCauldron.class)); + cards.add(new SetCardInfo("Storm Crow", "36a", Rarity.COMMON, mage.cards.s.StormCrow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm Crow", "36b", Rarity.COMMON, mage.cards.s.StormCrow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm Shaman", "81a", Rarity.COMMON, mage.cards.s.StormShaman.class)); + cards.add(new SetCardInfo("Storm Shaman", "81b", Rarity.COMMON, mage.cards.s.StormShaman.class)); + cards.add(new SetCardInfo("Stromgald Spy", 62, Rarity.UNCOMMON, mage.cards.s.StromgaldSpy.class)); + cards.add(new SetCardInfo("Surge of Strength", 109, Rarity.UNCOMMON, mage.cards.s.SurgeOfStrength.class)); + cards.add(new SetCardInfo("Sustaining Spirit", 18, Rarity.RARE, mage.cards.s.SustainingSpirit.class)); + cards.add(new SetCardInfo("Swamp Mosquito", "63a", Rarity.COMMON, mage.cards.s.SwampMosquito.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp Mosquito", "63b", Rarity.COMMON, mage.cards.s.SwampMosquito.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thawing Glaciers", 144, Rarity.RARE, mage.cards.t.ThawingGlaciers.class)); + cards.add(new SetCardInfo("Thought Lash", 39, Rarity.RARE, mage.cards.t.ThoughtLash.class)); + cards.add(new SetCardInfo("Tidal Control", 40, Rarity.RARE, mage.cards.t.TidalControl.class)); + cards.add(new SetCardInfo("Tornado", 101, Rarity.RARE, mage.cards.t.Tornado.class)); + cards.add(new SetCardInfo("Unlikely Alliance", 20, Rarity.UNCOMMON, mage.cards.u.UnlikelyAlliance.class)); + cards.add(new SetCardInfo("Urza's Engine", 135, Rarity.UNCOMMON, mage.cards.u.UrzasEngine.class)); + cards.add(new SetCardInfo("Varchild's Crusader", "82a", Rarity.COMMON, mage.cards.v.VarchildsCrusader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Varchild's Crusader", "82b", Rarity.COMMON, mage.cards.v.VarchildsCrusader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Varchild's War-Riders", 83, Rarity.RARE, mage.cards.v.VarchildsWarRiders.class)); + cards.add(new SetCardInfo("Veteran's Voice", "84a", Rarity.COMMON, mage.cards.v.VeteransVoice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Veteran's Voice", "84b", Rarity.COMMON, mage.cards.v.VeteransVoice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Viscerid Armor", "41a", Rarity.COMMON, mage.cards.v.VisceridArmor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Viscerid Armor", "41b", Rarity.COMMON, mage.cards.v.VisceridArmor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Viscerid Drone", 42, Rarity.UNCOMMON, mage.cards.v.VisceridDrone.class)); + cards.add(new SetCardInfo("Wandering Mage", 111, Rarity.RARE, mage.cards.w.WanderingMage.class)); + cards.add(new SetCardInfo("Whip Vine", "103a", Rarity.COMMON, mage.cards.w.WhipVine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Whip Vine", "103b", Rarity.COMMON, mage.cards.w.WhipVine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Whirling Catapult", 136, Rarity.UNCOMMON, mage.cards.w.WhirlingCatapult.class)); + cards.add(new SetCardInfo("Wild Aesthir", "21a", Rarity.COMMON, mage.cards.w.WildAesthir.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wild Aesthir", "21b", Rarity.COMMON, mage.cards.w.WildAesthir.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Winter's Night", 114, Rarity.RARE, mage.cards.w.WintersNight.class)); + cards.add(new SetCardInfo("Yavimaya Ancients", "104a", Rarity.COMMON, mage.cards.y.YavimayaAncients.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yavimaya Ancients", "104b", Rarity.COMMON, mage.cards.y.YavimayaAncients.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yavimaya Ants", 105, Rarity.UNCOMMON, mage.cards.y.YavimayaAnts.class)); } } diff --git a/Mage.Sets/src/mage/sets/IceAge.java b/Mage.Sets/src/mage/sets/IceAge.java index 4393e0f21e4..50884d78997 100644 --- a/Mage.Sets/src/mage/sets/IceAge.java +++ b/Mage.Sets/src/mage/sets/IceAge.java @@ -311,6 +311,7 @@ public class IceAge extends ExpansionSet { cards.add(new SetCardInfo("Soul Burn", 49, Rarity.COMMON, mage.cards.s.SoulBurn.class)); cards.add(new SetCardInfo("Soul Kiss", 50, Rarity.COMMON, mage.cards.s.SoulKiss.class)); cards.add(new SetCardInfo("Spoils of Evil", 51, Rarity.RARE, mage.cards.s.SpoilsOfEvil.class)); + cards.add(new SetCardInfo("Staff of the Ages", 315, Rarity.RARE, mage.cards.s.StaffOfTheAges.class)); cards.add(new SetCardInfo("Stampede", 153, Rarity.RARE, mage.cards.s.Stampede.class)); cards.add(new SetCardInfo("Stonehands", 219, Rarity.COMMON, mage.cards.s.Stonehands.class)); cards.add(new SetCardInfo("Stone Rain", 217, Rarity.COMMON, mage.cards.s.StoneRain.class)); diff --git a/Mage.Sets/src/mage/sets/Judgment.java b/Mage.Sets/src/mage/sets/Judgment.java index fa99c1a1b31..3ba00a1bfa9 100644 --- a/Mage.Sets/src/mage/sets/Judgment.java +++ b/Mage.Sets/src/mage/sets/Judgment.java @@ -130,6 +130,7 @@ public class Judgment extends ExpansionSet { cards.add(new SetCardInfo("Mirari's Wake", 139, Rarity.RARE, mage.cards.m.MirarisWake.class)); cards.add(new SetCardInfo("Mirror Wall", 47, Rarity.COMMON, mage.cards.m.MirrorWall.class)); cards.add(new SetCardInfo("Mist of Stagnation", 48, Rarity.RARE, mage.cards.m.MistOfStagnation.class)); + cards.add(new SetCardInfo("Morality Shift", 70, Rarity.RARE, mage.cards.m.MoralityShift.class)); cards.add(new SetCardInfo("Nantuko Monastery", 142, Rarity.UNCOMMON, mage.cards.n.NantukoMonastery.class)); cards.add(new SetCardInfo("Nantuko Tracer", 125, Rarity.COMMON, mage.cards.n.NantukoTracer.class)); cards.add(new SetCardInfo("Nomad Mythmaker", 15, Rarity.RARE, mage.cards.n.NomadMythmaker.class)); diff --git a/Mage.Sets/src/mage/sets/Legends.java b/Mage.Sets/src/mage/sets/Legends.java index 779015f3cdf..3e9ab7b76a1 100644 --- a/Mage.Sets/src/mage/sets/Legends.java +++ b/Mage.Sets/src/mage/sets/Legends.java @@ -95,6 +95,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Concordant Crossroads", 93, Rarity.RARE, mage.cards.c.ConcordantCrossroads.class)); cards.add(new SetCardInfo("Cosmic Horror", 6, Rarity.RARE, mage.cards.c.CosmicHorror.class)); cards.add(new SetCardInfo("Craw Giant", 94, Rarity.UNCOMMON, mage.cards.c.CrawGiant.class)); + cards.add(new SetCardInfo("Crevasse", 138, Rarity.UNCOMMON, mage.cards.c.Crevasse.class)); cards.add(new SetCardInfo("Crimson Kobolds", 219, Rarity.COMMON, mage.cards.c.CrimsonKobolds.class)); cards.add(new SetCardInfo("Crimson Manticore", 139, Rarity.RARE, mage.cards.c.CrimsonManticore.class)); cards.add(new SetCardInfo("Crookshank Kobolds", 220, Rarity.COMMON, mage.cards.c.CrookshankKobolds.class)); @@ -102,6 +103,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Dakkon Blackblade", 265, Rarity.RARE, mage.cards.d.DakkonBlackblade.class)); cards.add(new SetCardInfo("Darkness", 8, Rarity.COMMON, mage.cards.d.Darkness.class)); cards.add(new SetCardInfo("D'Avenant Archer", 176, Rarity.COMMON, mage.cards.d.DAvenantArcher.class)); + cards.add(new SetCardInfo("Deadfall", 95, Rarity.UNCOMMON, mage.cards.d.Deadfall.class)); cards.add(new SetCardInfo("Demonic Torment", 9, Rarity.UNCOMMON, mage.cards.d.DemonicTorment.class)); cards.add(new SetCardInfo("Devouring Deep", 50, Rarity.COMMON, mage.cards.d.DevouringDeep.class)); cards.add(new SetCardInfo("Disharmony", 140, Rarity.RARE, mage.cards.d.Disharmony.class)); @@ -135,8 +137,10 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Glyph of Destruction", 148, Rarity.COMMON, mage.cards.g.GlyphOfDestruction.class)); cards.add(new SetCardInfo("Glyph of Doom", 14, Rarity.COMMON, mage.cards.g.GlyphOfDoom.class)); cards.add(new SetCardInfo("Glyph of Life", 184, Rarity.COMMON, mage.cards.g.GlyphOfLife.class)); + cards.add(new SetCardInfo("Gosta Dirk", 267, Rarity.RARE, mage.cards.g.GostaDirk.class)); cards.add(new SetCardInfo("Gravity Sphere", 149, Rarity.RARE, mage.cards.g.GravitySphere.class)); cards.add(new SetCardInfo("Great Defender", 185, Rarity.UNCOMMON, mage.cards.g.GreatDefender.class)); + cards.add(new SetCardInfo("Great Wall", 186, Rarity.UNCOMMON, mage.cards.g.GreatWall.class)); cards.add(new SetCardInfo("Greater Realm of Preservation", 187, Rarity.UNCOMMON, mage.cards.g.GreaterRealmOfPreservation.class)); cards.add(new SetCardInfo("Greed", 15, Rarity.RARE, mage.cards.g.Greed.class)); cards.add(new SetCardInfo("Green Mana Battery", 223, Rarity.UNCOMMON, mage.cards.g.GreenManaBattery.class)); @@ -189,6 +193,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Lifeblood", 196, Rarity.RARE, mage.cards.l.Lifeblood.class)); cards.add(new SetCardInfo("Living Plane", 107, Rarity.RARE, mage.cards.l.LivingPlane.class)); cards.add(new SetCardInfo("Livonya Silone", 282, Rarity.RARE, mage.cards.l.LivonyaSilone.class)); + cards.add(new SetCardInfo("Lord Magnus", 283, Rarity.UNCOMMON, mage.cards.l.LordMagnus.class)); cards.add(new SetCardInfo("Lost Soul", 25, Rarity.COMMON, mage.cards.l.LostSoul.class)); cards.add(new SetCardInfo("Mana Drain", 65, Rarity.UNCOMMON, mage.cards.m.ManaDrain.class)); cards.add(new SetCardInfo("Mana Matrix", 230, Rarity.RARE, mage.cards.m.ManaMatrix.class)); @@ -218,6 +223,7 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Psionic Entity", 67, Rarity.RARE, mage.cards.p.PsionicEntity.class)); cards.add(new SetCardInfo("Psychic Purge", 68, Rarity.COMMON, mage.cards.p.PsychicPurge.class)); cards.add(new SetCardInfo("Pyrotechnics", 158, Rarity.COMMON, mage.cards.p.Pyrotechnics.class)); + cards.add(new SetCardInfo("Quagmire", 29, Rarity.UNCOMMON, mage.cards.q.Quagmire.class)); cards.add(new SetCardInfo("Rabid Wombat", 112, Rarity.UNCOMMON, mage.cards.r.RabidWombat.class)); cards.add(new SetCardInfo("Radjan Spirit", 113, Rarity.UNCOMMON, mage.cards.r.RadjanSpirit.class)); cards.add(new SetCardInfo("Raging Bull", 160, Rarity.COMMON, mage.cards.r.RagingBull.class)); @@ -280,8 +286,10 @@ public class Legends extends ExpansionSet { cards.add(new SetCardInfo("Tuknir Deathlock", 307, Rarity.RARE, mage.cards.t.TuknirDeathlock.class)); cards.add(new SetCardInfo("Tundra Wolves", 209, Rarity.COMMON, mage.cards.t.TundraWolves.class)); cards.add(new SetCardInfo("Typhoon", 123, Rarity.RARE, mage.cards.t.Typhoon.class)); + cards.add(new SetCardInfo("Undertow", 82, Rarity.UNCOMMON, mage.cards.u.Undertow.class)); cards.add(new SetCardInfo("Underworld Dreams", 38, Rarity.UNCOMMON, mage.cards.u.UnderworldDreams.class)); cards.add(new SetCardInfo("Untamed Wilds", 124, Rarity.UNCOMMON, mage.cards.u.UntamedWilds.class)); + cards.add(new SetCardInfo("Ur-Drago", 308, Rarity.RARE, mage.cards.u.UrDrago.class)); cards.add(new SetCardInfo("Urborg", 255, Rarity.UNCOMMON, mage.cards.u.Urborg.class)); cards.add(new SetCardInfo("Vaevictis Asmadi", 309, Rarity.RARE, mage.cards.v.VaevictisAsmadi.class)); cards.add(new SetCardInfo("Vampire Bats", 39, Rarity.COMMON, mage.cards.v.VampireBats.class)); diff --git a/Mage.Sets/src/mage/sets/MercadianMasques.java b/Mage.Sets/src/mage/sets/MercadianMasques.java index 2b167bc1d48..0b52b888585 100644 --- a/Mage.Sets/src/mage/sets/MercadianMasques.java +++ b/Mage.Sets/src/mage/sets/MercadianMasques.java @@ -358,6 +358,7 @@ public class MercadianMasques extends ExpansionSet { cards.add(new SetCardInfo("Two-Headed Dragon", 221, Rarity.RARE, mage.cards.t.TwoHeadedDragon.class)); cards.add(new SetCardInfo("Undertaker", 167, Rarity.COMMON, mage.cards.u.Undertaker.class)); cards.add(new SetCardInfo("Unmask", 168, Rarity.RARE, mage.cards.u.Unmask.class)); + cards.add(new SetCardInfo("Uphill Battle", 222, Rarity.UNCOMMON, mage.cards.u.UphillBattle.class)); cards.add(new SetCardInfo("Vendetta", 170, Rarity.COMMON, mage.cards.v.Vendetta.class)); cards.add(new SetCardInfo("Venomous Dragonfly", 282, Rarity.COMMON, mage.cards.v.VenomousDragonfly.class)); cards.add(new SetCardInfo("Vernal Equinox", 283, Rarity.RARE, mage.cards.v.VernalEquinox.class)); diff --git a/Mage.Sets/src/mage/sets/Mirage.java b/Mage.Sets/src/mage/sets/Mirage.java index 27052f442cb..5db3aa9154a 100644 --- a/Mage.Sets/src/mage/sets/Mirage.java +++ b/Mage.Sets/src/mage/sets/Mirage.java @@ -79,6 +79,7 @@ public class Mirage extends ExpansionSet { cards.add(new SetCardInfo("Boomerang", 56, Rarity.COMMON, mage.cards.b.Boomerang.class)); cards.add(new SetCardInfo("Breathstealer", 7, Rarity.COMMON, mage.cards.b.Breathstealer.class)); cards.add(new SetCardInfo("Brushwagg", 106, Rarity.RARE, mage.cards.b.Brushwagg.class)); + cards.add(new SetCardInfo("Builder's Bane", 160, Rarity.COMMON, mage.cards.b.BuildersBane.class)); cards.add(new SetCardInfo("Burning Palm Efreet", 161, Rarity.UNCOMMON, mage.cards.b.BurningPalmEfreet.class)); cards.add(new SetCardInfo("Burning Shield Askari", 162, Rarity.COMMON, mage.cards.b.BurningShieldAskari.class)); cards.add(new SetCardInfo("Cadaverous Bloom", 318, Rarity.RARE, mage.cards.c.CadaverousBloom.class)); diff --git a/Mage.Sets/src/mage/sets/StarWars.java b/Mage.Sets/src/mage/sets/StarWars.java index 3d84487942c..0959f928fde 100644 --- a/Mage.Sets/src/mage/sets/StarWars.java +++ b/Mage.Sets/src/mage/sets/StarWars.java @@ -27,7 +27,9 @@ */ package mage.sets; +import mage.cards.CardGraphicInfo; import mage.cards.ExpansionSet; +import mage.cards.FrameStyle; import mage.constants.Rarity; import mage.constants.SetType; @@ -128,10 +130,10 @@ public class StarWars extends ExpansionSet { cards.add(new SetCardInfo("Force Reflex", 13, Rarity.COMMON, mage.cards.f.ForceReflex.class)); cards.add(new SetCardInfo("Force Scream", 104, Rarity.UNCOMMON, mage.cards.f.ForceScream.class)); cards.add(new SetCardInfo("Force Spark", 105, Rarity.COMMON, mage.cards.f.ForceSpark.class)); - cards.add(new SetCardInfo("Forest", 268, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Forest", 269, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Forest", 270, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Forest", 271, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 268, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Forest", 269, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Forest", 270, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Forest", 271, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); cards.add(new SetCardInfo("Fulfill Contract", 224, Rarity.COMMON, mage.cards.f.FulfillContract.class)); cards.add(new SetCardInfo("Gamorrean Prison Guard", 106, Rarity.UNCOMMON, mage.cards.g.GamorreanPrisonGuard.class)); cards.add(new SetCardInfo("General Grievous", 185, Rarity.MYTHIC, mage.cards.g.GeneralGrievous.class)); @@ -158,10 +160,10 @@ public class StarWars extends ExpansionSet { cards.add(new SetCardInfo("Interrogation", 81, Rarity.COMMON, mage.cards.i.Interrogation.class)); cards.add(new SetCardInfo("Ion Cannon", 15, Rarity.COMMON, mage.cards.i.IonCannon.class)); cards.add(new SetCardInfo("Iron Fist of the Empire", 191, Rarity.RARE, mage.cards.i.IronFistOfTheEmpire.class)); - cards.add(new SetCardInfo("Island", 256, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Island", 257, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Island", 258, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Island", 259, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 256, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Island", 257, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Island", 258, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Island", 259, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); cards.add(new SetCardInfo("Ithorian Initiate", 140, Rarity.COMMON, mage.cards.i.IthorianInitiate.class)); cards.add(new SetCardInfo("Jabba the Hutt", 192, Rarity.RARE, mage.cards.j.JabbaTheHutt.class)); cards.add(new SetCardInfo("Jango Fett", 111, Rarity.RARE, mage.cards.j.JangoFett.class)); @@ -203,10 +205,10 @@ public class StarWars extends ExpansionSet { cards.add(new SetCardInfo("Moisture Farm", 247, Rarity.UNCOMMON, mage.cards.m.MoistureFarm.class)); cards.add(new SetCardInfo("Mon Calamari Cruiser", 48, Rarity.UNCOMMON, mage.cards.m.MonCalamariCruiser.class)); cards.add(new SetCardInfo("Mon Calamari Initiate", 49, Rarity.COMMON, mage.cards.m.MonCalamariInitiate.class)); - cards.add(new SetCardInfo("Mountain", 264, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 265, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 266, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 267, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 264, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Mountain", 265, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Mountain", 266, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Mountain", 267, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); cards.add(new SetCardInfo("N-1 Starfighter", 225, Rarity.COMMON, mage.cards.n.N1Starfighter.class)); cards.add(new SetCardInfo("Nebulon-B Frigate", 25, Rarity.COMMON, mage.cards.n.NebulonBFrigate.class)); cards.add(new SetCardInfo("Neophyte Hateflayer", 82, Rarity.COMMON, mage.cards.n.NeophyteHateflayer.class)); @@ -224,10 +226,10 @@ public class StarWars extends ExpansionSet { cards.add(new SetCardInfo("Outer Rim Slaver", 201, Rarity.COMMON, mage.cards.o.OuterRimSlaver.class)); cards.add(new SetCardInfo("Outlaw Holocron", 235, Rarity.COMMON, mage.cards.o.OutlawHolocron.class)); cards.add(new SetCardInfo("Personal Energy Shield", 51, Rarity.COMMON, mage.cards.p.PersonalEnergyShield.class)); - cards.add(new SetCardInfo("Plains", 252, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Plains", 253, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Plains", 254, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Plains", 255, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 252, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Plains", 253, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Plains", 254, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Plains", 255, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); cards.add(new SetCardInfo("Plo Koon", 27, Rarity.RARE, mage.cards.p.PloKoon.class)); cards.add(new SetCardInfo("Precipice of Mortis", 202, Rarity.RARE, mage.cards.p.PrecipiceOfMortis.class)); cards.add(new SetCardInfo("Predator's Strike", 151, Rarity.COMMON, mage.cards.p.PredatorsStrike.class)); @@ -287,10 +289,10 @@ public class StarWars extends ExpansionSet { cards.add(new SetCardInfo("Strike Team Commando", 227, Rarity.COMMON, mage.cards.s.StrikeTeamCommando.class)); cards.add(new SetCardInfo("Super Battle Droid", 59, Rarity.COMMON, mage.cards.s.SuperBattleDroid.class)); cards.add(new SetCardInfo("Surprise Maneuver", 60, Rarity.COMMON, mage.cards.s.SurpriseManeuver.class)); - cards.add(new SetCardInfo("Swamp", 260, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 261, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 262, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 263, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 260, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Swamp", 261, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Swamp", 262, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); + cards.add(new SetCardInfo("Swamp", 263, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true))); cards.add(new SetCardInfo("Swarm the Skies", 92, Rarity.COMMON, mage.cards.s.SwarmTheSkies.class)); cards.add(new SetCardInfo("Syndicate Enforcer", 124, Rarity.COMMON, mage.cards.s.SyndicateEnforcerSWS.class)); cards.add(new SetCardInfo("Tank Droid", 218, Rarity.RARE, mage.cards.t.TankDroid.class)); diff --git a/Mage.Sets/src/mage/sets/Unhinged.java b/Mage.Sets/src/mage/sets/Unhinged.java index 222281febeb..b4e4aedb0d4 100644 --- a/Mage.Sets/src/mage/sets/Unhinged.java +++ b/Mage.Sets/src/mage/sets/Unhinged.java @@ -20,10 +20,12 @@ public class Unhinged extends ExpansionSet { private Unhinged() { super("Unhinged", "UNH", ExpansionSet.buildDate(2004, 11, 20), SetType.JOKESET); + cards.add(new SetCardInfo("Forest", 140, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Island", 137, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Johnny, Combo Player", 35, Rarity.RARE, mage.cards.j.JohnnyComboPlayer.class)); cards.add(new SetCardInfo("Mountain", 139, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); + cards.add(new SetCardInfo("Mox Lotus", 124, Rarity.RARE, mage.cards.m.MoxLotus.class)); cards.add(new SetCardInfo("Plains", 136, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Swamp", 138, Rarity.LAND, mage.cards.basiclands.Swamp.class, new CardGraphicInfo(FrameStyle.UNH_FULL_ART_BASIC, false))); } diff --git a/Mage.Stats/pom.xml b/Mage.Stats/pom.xml index 31026c30623..52e30f81369 100644 --- a/Mage.Stats/pom.xml +++ b/Mage.Stats/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.27 + 1.4.28 org.mage diff --git a/Mage.Tests/pom.xml b/Mage.Tests/pom.xml index e3be5163001..8fe7714626f 100644 --- a/Mage.Tests/pom.xml +++ b/Mage.Tests/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.27 + 1.4.28 mage-tests 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 index fd1195e28be..7a622b726bd 100644 --- 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 @@ -29,7 +29,6 @@ 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; @@ -58,7 +57,6 @@ public class CastCreaturesTest extends CardTestPlayerBaseAI { * first creature */ @Test - @Ignore // TODO: find out why sometimes Produces error probably because of wrong mana usage of the AI - Not solved yet public void testSimpleCast2() { addCard(Zone.HAND, playerA, "Silvercoat Lion"); @@ -194,4 +192,43 @@ public class CastCreaturesTest extends CardTestPlayerBaseAI { assertPermanentCount(playerA, "Bog Wraith", 1); } + + /** + * Tests that the creature is cast if enough mana is available. + * + * Once Ammit Eternal is cast against a computer AI opponent, the AI just + * decides to sit there and only play basic lands. I've sat there and decked + * it because it just plays lands. It's like it views giving the Ammit + * Eternal -1/-1 counters as a good thing for me, so it never casts any + * spells once Ammit Eternal hits the battlefield. + */ + @Test + public void testSimpleCastWithAmmitEternal() { + addCard(Zone.HAND, playerA, "Silvercoat Lion"); + addCard(Zone.HAND, playerA, "Plains", 2); + addCard(Zone.LIBRARY, playerA, "Plains", 2); + skipInitShuffling(); + + // Afflict 3 (Whenever this creature becomes blocked, defending player loses 3 life.) + // Whenever an opponent casts a spell, put a -1/-1 counter on Ammit Eternal. + // Whenever Ammit Eternal deals combat damage to a player, remove all -1/-1 counters from it. + addCard(Zone.HAND, playerB, "Ammit Eternal"); // Creature {2}{B} 5/5 + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Ammit Eternal"); + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, "Ammit Eternal", 1); + + assertPermanentCount(playerA, "Plains", 2); + assertHandCount(playerA, "Plains", 1); + assertHandCount(playerA, "Silvercoat Lion", 0); + assertPermanentCount(playerA, 3); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + + assertPowerToughness(playerB, "Ammit Eternal", 4, 4); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/CastSplitCardsFromOtherZonesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/CastSplitCardsFromOtherZonesTest.java index fcec6f5e2ca..d6430520c35 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/CastSplitCardsFromOtherZonesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/CastSplitCardsFromOtherZonesTest.java @@ -124,4 +124,35 @@ public class CastSplitCardsFromOtherZonesTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Icy Manipulator", 1); } + + /** + * Cast a split card half from exile + */ + @Test + public void testCastSpliHalfFromExile() { + // Fire Instant {1}{R} + // Fire deals 2 damage divided as you choose among one or two target creatures and/or players. + // Ice Instant {1}{U} + // Tap target permanent. + // Draw a card. + addCard(Zone.LIBRARY, playerA, "Fire // Ice", 1); + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // Creature 2/2 + + // Whenever Etali, Primal Storm attacks, exile the top card of each player's library, then you may + // cast any number of nonland cards exiled this way without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerB, "Etali, Primal Storm"); // Creature {4}{R} 6/6 + + attack(2, playerB, "Etali, Primal Storm"); + setChoice(playerB, "Yes"); + setChoice(playerB, "Cast Fire"); + addTarget(playerB, "Silvercoat Lion"); + + setStopAt(2, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerA, 14); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertGraveyardCount(playerA, "Fire // Ice", 1); + } } diff --git a/Mage.Updater/pom.xml b/Mage.Updater/pom.xml index 41c5d2a9ea6..f815ad79139 100644 --- a/Mage.Updater/pom.xml +++ b/Mage.Updater/pom.xml @@ -5,7 +5,7 @@ mage-root org.mage - 1.4.27 + 1.4.28 4.0.0 diff --git a/Mage.Updater/src/main/java/com/magefree/update/Updater.java b/Mage.Updater/src/main/java/com/magefree/update/Updater.java index ed39bc80b92..fdceb244442 100644 --- a/Mage.Updater/src/main/java/com/magefree/update/Updater.java +++ b/Mage.Updater/src/main/java/com/magefree/update/Updater.java @@ -1,6 +1,6 @@ package com.magefree.update; -import com.magefree.update.helpers.ChechsumHelper; +import com.magefree.update.helpers.ChecksumHelper; import com.magefree.update.helpers.FileHelper; import java.io.File; @@ -67,7 +67,7 @@ public class Updater { public HashMap readLocalData() throws Exception { HashMap result = new HashMap<>(); for (File f : findFiles()) { - result.put(f.getPath().replaceAll("\\\\", "/"), ChechsumHelper.getSHA1Checksum(f.getPath())); + result.put(f.getPath().replaceAll("\\\\", "/"), ChecksumHelper.getSHA1Checksum(f.getPath())); } return result; } diff --git a/Mage.Updater/src/main/java/com/magefree/update/helpers/ChechsumHelper.java b/Mage.Updater/src/main/java/com/magefree/update/helpers/ChecksumHelper.java similarity index 97% rename from Mage.Updater/src/main/java/com/magefree/update/helpers/ChechsumHelper.java rename to Mage.Updater/src/main/java/com/magefree/update/helpers/ChecksumHelper.java index c4d035eeedf..c815ede0dbd 100644 --- a/Mage.Updater/src/main/java/com/magefree/update/helpers/ChechsumHelper.java +++ b/Mage.Updater/src/main/java/com/magefree/update/helpers/ChecksumHelper.java @@ -7,7 +7,7 @@ import java.security.MessageDigest; /** * @author Loki */ -public final class ChechsumHelper { +public final class ChecksumHelper { public static byte[] createChecksum(String filename) throws Exception { InputStream fis = null; diff --git a/Mage.Verify/pom.xml b/Mage.Verify/pom.xml index 2adf2aa8813..fd26b6e7f5a 100644 --- a/Mage.Verify/pom.xml +++ b/Mage.Verify/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.27 + 1.4.28 mage-verify diff --git a/Mage/pom.xml b/Mage/pom.xml index ea4d465ca1d..c3630a9fdd1 100644 --- a/Mage/pom.xml +++ b/Mage/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.27 + 1.4.28 mage diff --git a/Mage/src/main/java/mage/Mana.java b/Mage/src/main/java/mage/Mana.java index 0e9aaf28253..32e57e8d45d 100644 --- a/Mage/src/main/java/mage/Mana.java +++ b/Mage/src/main/java/mage/Mana.java @@ -518,25 +518,46 @@ public class Mana implements Comparable, Serializable, Copyable { if (generic > 0) { sbMana.append('{').append(Integer.toString(generic)).append('}'); } - for (int i = 0; i < colorless; i++) { + if (colorless >= 20) { + sbMana.append(Integer.toString(colorless)).append("{C}"); + } + if (white >= 20) { + sbMana.append(Integer.toString(white)).append("{W}"); + } + if (blue >= 20) { + sbMana.append(Integer.toString(blue)).append("{U}"); + } + if (black >= 20) { + sbMana.append(Integer.toString(black)).append("{B}"); + } + if (red >= 20) { + sbMana.append(Integer.toString(red)).append("{R}"); + } + if (green >= 20) { + sbMana.append(Integer.toString(green)).append("{G}"); + } + if (any >= 20) { + sbMana.append(Integer.toString(any)).append("{Any}"); + } + for (int i = 0; i < colorless && colorless < 20; i++) { sbMana.append("{C}"); } - for (int i = 0; i < white; i++) { + for (int i = 0; i < white && white < 20; i++) { sbMana.append("{W}"); } - for (int i = 0; i < blue; i++) { + for (int i = 0; i < blue && blue < 20; i++) { sbMana.append("{U}"); } - for (int i = 0; i < black; i++) { + for (int i = 0; i < black && black < 20; i++) { sbMana.append("{B}"); } - for (int i = 0; i < red; i++) { + for (int i = 0; i < red && red < 20; i++) { sbMana.append("{R}"); } - for (int i = 0; i < green; i++) { + for (int i = 0; i < green && green < 20; i++) { sbMana.append("{G}"); } - for (int i = 0; i < any; i++) { + for (int i = 0; i < any && any < 20; i++) { sbMana.append("{Any}"); } return sbMana.toString(); diff --git a/Mage/src/main/java/mage/abilities/common/GainLifeControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/GainLifeControllerTriggeredAbility.java index 234f08ef782..b06fb6149da 100644 --- a/Mage/src/main/java/mage/abilities/common/GainLifeControllerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/GainLifeControllerTriggeredAbility.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.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -39,14 +38,14 @@ import mage.target.targetpointer.FixedTarget; * * @author LevelX2 */ - public class GainLifeControllerTriggeredAbility extends TriggeredAbilityImpl { + private boolean setTargetPointer; public GainLifeControllerTriggeredAbility(Effect effect, boolean optional) { this(effect, optional, false); } - + public GainLifeControllerTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) { super(Zone.BATTLEFIELD, effect, optional); this.setTargetPointer = setTargetPointer; @@ -72,8 +71,8 @@ public class GainLifeControllerTriggeredAbility extends TriggeredAbilityImpl { if (event.getPlayerId().equals(this.getControllerId())) { if (setTargetPointer) { for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getPlayerId())); - effect.setValue("gainedLife", event.getAmount()); + effect.setTargetPointer(new FixedTarget(event.getPlayerId())); + effect.setValue("gainedLife", event.getAmount()); } } return true; @@ -83,7 +82,7 @@ public class GainLifeControllerTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return new StringBuilder("Whenever you gain life, ").append(super.getRule()).toString() ; + return new StringBuilder("Whenever you gain life, ").append(super.getRule()).toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java index 5bcd0ff0398..6070e4693de 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java @@ -111,7 +111,7 @@ public class ContinuousEffects implements Serializable { costModificationEffects = effect.costModificationEffects.copy(); spliceCardEffects = effect.spliceCardEffects.copy(); - for (Map.Entry> entry: effect.temporaryEffects.entrySet()) { + for (Map.Entry> entry : effect.temporaryEffects.entrySet()) { temporaryEffects.put(entry.getKey().copy(), entry.getValue()); } collectAllEffects(); @@ -352,9 +352,11 @@ public class ContinuousEffects implements Serializable { continue; } if (event.getAppliedEffects() != null && event.getAppliedEffects().contains(effect.getId())) { - // Effect already applied to this event, ignore it - // TODO: Handle also gained effect that are connected to different abilities. - continue; + if (!(effect instanceof CommanderReplacementEffect)) { // 903.9. + // Effect already applied to this event, ignore it + // TODO: Handle also gained effect that are connected to different abilities. + continue; + } } Set abilities = replacementEffects.getAbility(effect.getId()); Set applicableAbilities = new HashSet<>(); @@ -752,7 +754,7 @@ public class ContinuousEffects implements Serializable { // Remove all consumed effects (ability dependant) for (Iterator it1 = rEffects.keySet().iterator(); it1.hasNext();) { ReplacementEffect entry = it1.next(); - if (consumed.containsKey(entry.getId())) { + if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9. Set consumedAbilitiesIds = consumed.get(entry.getId()); if (rEffects.get(entry) == null || consumedAbilitiesIds.size() == rEffects.get(entry).size()) { it1.remove(); diff --git a/Mage/src/main/java/mage/abilities/effects/Effect.java b/Mage/src/main/java/mage/abilities/effects/Effect.java index fc98f40a947..cc7d51c7b34 100644 --- a/Mage/src/main/java/mage/abilities/effects/Effect.java +++ b/Mage/src/main/java/mage/abilities/effects/Effect.java @@ -46,12 +46,34 @@ public interface Effect extends Serializable { void newId(); + /** + * Some general behaviours for rule text handling: Rule text of effects get + * automatically a full stop "." at the end, if not another effect e.g. with + * a starting "and" follows. So at least for effects of the framework, that + * are used from multiple cards, it's better to set no full stop at the end + * of the rule text of an effect. Also the starting letter of an effect is + * automatically converted to upper case if the rule text starts with this + * text. So There is no need to let the effect text start with upper case, + * even if extracted from a filter message. Also here it's important to use + * only lower cases for effects located in the framework, so if used for a + * triggered abilitiy, the effect text needs to start with lower case after + * the comma. + * + * @param mode the selected mode of the ability (mostly there is only one) + * @return + */ String getText(Mode mode); Effect setText(String staticText); boolean apply(Game game, Ability source); + /** + * The outcome is used for the AI to decide if an effect does bad or good to + * the targets. + * + * @return + */ Outcome getOutcome(); void setOutcome(Outcome outcome); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java index 486e5ed7446..adeb100ccdf 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java @@ -49,6 +49,9 @@ import mage.players.Player; /* * 903.11. If a commander would be put into its owner’s graveyard from anywhere, that player may put it into the command zone instead. * 903.12. If a commander would be put into the exile zone from anywhere, its owner may put it into the command zone instead. + * 903.9. If a commander would be exiled from anywhere or put into its owner’s hand, graveyard, or +library from anywhere, its owner may put it into the command zone instead. This replacement effect +may apply more than once to the same event. This is an exception to rule 614.5. */ public class CommanderReplacementEffect extends ReplacementEffectImpl { @@ -138,11 +141,10 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl { if (permanent != null) { Player player = game.getPlayer(permanent.getOwnerId()); if (player != null && player.chooseUse(Outcome.Benefit, "Move commander to command zone?", source, game)) { - boolean result = permanent.moveToZone(Zone.COMMAND, source.getSourceId(), game, false); + ((ZoneChangeEvent) event).setToZone(Zone.COMMAND); if (!game.isSimulation()) { game.informPlayers(player.getLogName() + " has moved his or her commander to the command zone"); } - return result; } } } else { @@ -159,11 +161,10 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl { if (card != null) { Player player = game.getPlayer(card.getOwnerId()); if (player != null && player.chooseUse(Outcome.Benefit, "Move commander to command zone?", source, game)) { - boolean result = card.moveToZone(Zone.COMMAND, source.getSourceId(), game, false); + ((ZoneChangeEvent) event).setToZone(Zone.COMMAND); if (!game.isSimulation()) { game.informPlayers(player.getLogName() + " has moved his or her commander to the command zone"); } - return result; } } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CantHaveCountersSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CantHaveCountersSourceEffect.java index e6ca45a18b4..2c8a4818a9f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CantHaveCountersSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ruleModifying/CantHaveCountersSourceEffect.java @@ -1,71 +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.abilities.effects.common.ruleModifying; - -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.GameEvent; - -/** - * - * @author Styxo - */ -public class CantHaveCountersSourceEffect extends ContinuousRuleModifyingEffectImpl { - - public CantHaveCountersSourceEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "{this} can't have counters placed on it"; - } - - public CantHaveCountersSourceEffect(final CantHaveCountersSourceEffect effect) { - super(effect); - } - - @Override - public CantHaveCountersSourceEffect copy() { - return new CantHaveCountersSourceEffect(this); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ADD_COUNTERS; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - UUID sourceId = source.getSourceId(); - if (sourceId != null) { - return sourceId.equals(event.getTargetId()); - } - return false; - } -} +/* + * 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.ruleModifying; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author Styxo + */ +public class CantHaveCountersSourceEffect extends ContinuousRuleModifyingEffectImpl { + + public CantHaveCountersSourceEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment); + staticText = "{this} can't have counters put on it"; + } + + public CantHaveCountersSourceEffect(final CantHaveCountersSourceEffect effect) { + super(effect); + } + + @Override + public CantHaveCountersSourceEffect copy() { + return new CantHaveCountersSourceEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ADD_COUNTERS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + UUID sourceId = source.getSourceId(); + if (sourceId != null) { + return sourceId.equals(event.getTargetId()); + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/LandwalkAbility.java b/Mage/src/main/java/mage/abilities/keyword/LandwalkAbility.java index 2ff19c332b6..bf382c72799 100644 --- a/Mage/src/main/java/mage/abilities/keyword/LandwalkAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/LandwalkAbility.java @@ -30,6 +30,7 @@ package mage.abilities.keyword; import mage.abilities.Ability; import mage.abilities.EvasionAbility; import mage.abilities.effects.RestrictionEffect; +import mage.constants.AsThoughEffectType; import mage.constants.Duration; import mage.filter.common.FilterLandPermanent; import mage.game.Game; @@ -86,7 +87,25 @@ class LandwalkEffect extends RestrictionEffect { @Override public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game) { - return !game.getBattlefield().contains(filter, blocker.getControllerId(), 1, game); + if (game.getBattlefield().contains(filter, blocker.getControllerId(), 1, game) + && !game.getContinuousEffects().asThough(blocker.getId(), AsThoughEffectType.BLOCK_LANDWALK, source, blocker.getControllerId(), game)) { + switch (filter.getMessage()) { + case "plains": + return game.getContinuousEffects().asThough(blocker.getId(), AsThoughEffectType.BLOCK_PLAINSWALK, source, blocker.getControllerId(), game); + case "island": + return game.getContinuousEffects().asThough(blocker.getId(), AsThoughEffectType.BLOCK_ISLANDWALK, source, blocker.getControllerId(), game); + case "swamp": + return game.getContinuousEffects().asThough(blocker.getId(), AsThoughEffectType.BLOCK_SWAMPWALK, source, blocker.getControllerId(), game); + case "mountain": + return game.getContinuousEffects().asThough(blocker.getId(), AsThoughEffectType.BLOCK_MOUNTAINWALK, source, blocker.getControllerId(), game); + case "forest": + return game.getContinuousEffects().asThough(blocker.getId(), AsThoughEffectType.BLOCK_FORESTWALK, source, blocker.getControllerId(), game); + default: + return false; + + } + } + return true; } @Override @@ -104,25 +123,25 @@ class LandwalkEffect extends RestrictionEffect { StringBuilder sb = new StringBuilder(); sb.append(filter.getMessage()).append("walk"); if (withHintText) { - sb.append(" (This creature can't be blocked as long as defending player controls a "); + sb.append(" (This creature can't be blocked as long as defending player controls "); switch (filter.getMessage()) { case "swamp": - sb.append("Swamp"); + sb.append("a Swamp"); break; case "plains": - sb.append("Plains"); + sb.append("a Plains"); break; case "mountain": - sb.append("Mountain"); + sb.append("a Mountain"); break; case "forest": - sb.append("Forest"); + sb.append("a Forest"); break; case "island": - sb.append("Island"); + sb.append("an Island"); break; default: - sb.append(filter.getMessage()); + sb.append("a " + filter.getMessage()); } sb.append(".)"); diff --git a/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java b/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java index deef93c61bb..239706abad4 100644 --- a/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java @@ -44,8 +44,6 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; /** * @@ -158,11 +156,7 @@ class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent permanent = game.getPermanent(source.getSourceId()); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && permanent != null) { - controller.moveCardToExileWithInfo(permanent, null, "", source.getSourceId(), game, Zone.BATTLEFIELD, true); - } - return true; + ((ZoneChangeEvent) event).setToZone(Zone.EXILED); + return false; } } diff --git a/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java b/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java index 093dedb048c..0163abd7a67 100644 --- a/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/SimpleManaAbility.java @@ -53,7 +53,7 @@ public class SimpleManaAbility extends ActivatedManaAbilityImpl { * @param zone * @param effect * @param cost - * @param predictable set to false if definig the mana type or amount needs + * @param predictable set to false if defining the mana type or amount needs * to reveal information and can't be predicted */ public SimpleManaAbility(Zone zone, ManaEffect effect, Cost cost, boolean predictable) { diff --git a/Mage/src/main/java/mage/constants/AsThoughEffectType.java b/Mage/src/main/java/mage/constants/AsThoughEffectType.java index 50f7caeb4f8..7afa2c60002 100644 --- a/Mage/src/main/java/mage/constants/AsThoughEffectType.java +++ b/Mage/src/main/java/mage/constants/AsThoughEffectType.java @@ -12,6 +12,12 @@ public enum AsThoughEffectType { BLOCK_TAPPED, BLOCK_SHADOW, BLOCK_DRAGON, + BLOCK_LANDWALK, + BLOCK_PLAINSWALK, + BLOCK_ISLANDWALK, + BLOCK_SWAMPWALK, + BLOCK_MOUNTAINWALK, + BLOCK_FORESTWALK, BE_BLOCKED, PLAY_FROM_NOT_OWN_HAND_ZONE, CAST_AS_INSTANT, diff --git a/Mage/src/main/java/mage/game/match/MatchOptions.java b/Mage/src/main/java/mage/game/match/MatchOptions.java index 5b1f4a6df9d..e0dbb153fa5 100644 --- a/Mage/src/main/java/mage/game/match/MatchOptions.java +++ b/Mage/src/main/java/mage/game/match/MatchOptions.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.game.match; import mage.constants.MatchTimeLimit; @@ -61,6 +60,7 @@ public class MatchOptions implements Serializable { protected String password; protected SkillLevel skillLevel; protected boolean rollbackTurnsAllowed; + protected boolean spectatorsAllowed; protected int quitRatio; protected int edhPowerLevel; protected boolean rated; @@ -79,8 +79,7 @@ public class MatchOptions implements Serializable { this.multiPlayer = false; this.numSeats = 2; }*/ - - public MatchOptions(String name, String gameType, boolean multiPlayer, int numSeats ) { + public MatchOptions(String name, String gameType, boolean multiPlayer, int numSeats) { this.name = name; this.gameType = gameType; this.password = ""; @@ -178,7 +177,7 @@ public class MatchOptions implements Serializable { public MatchTimeLimit getMatchTimeLimit() { return this.matchTimeLimit; } - + public void setMatchTimeLimit(MatchTimeLimit matchTimeLimit) { this.matchTimeLimit = matchTimeLimit; } @@ -207,6 +206,14 @@ public class MatchOptions implements Serializable { this.rollbackTurnsAllowed = rollbackTurnsAllowed; } + public boolean isSpectatorsAllowed() { + return spectatorsAllowed; + } + + public void setSpectatorsAllowed(boolean spectatorsAllowed) { + this.spectatorsAllowed = spectatorsAllowed; + } + public int getQuitRatio() { return quitRatio; } @@ -214,7 +221,7 @@ public class MatchOptions implements Serializable { public void setQuitRatio(int quitRatio) { this.quitRatio = quitRatio; } - + public int getEdhPowerLevel() { return edhPowerLevel; } diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index 8700e560df7..8b9273e824a 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -59,7 +59,6 @@ public interface Permanent extends Card, Controllable { * @param tapped * @deprecated */ - @Deprecated void setTapped(boolean tapped); boolean canTap(); diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index b947530716b..47c1dba1266 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -731,7 +731,8 @@ public interface Player extends MageItem, Copyable { /** * Uses card.moveToExile and posts a inform message about moving the card to - * exile into the game log + * exile into the game log. Don't use this in replacement effects, because + * list of applied effects is not saved * * @param card * @param exileId exile zone id (optional) diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index b47fab6df90..fe798732bdc 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1097,6 +1097,8 @@ public abstract class PlayerImpl implements Player, Serializable { if (!ignoreTiming && !playLandAbility.canActivate(this.playerId, game)) { return false; } + + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId)); //20091005 - 305.1 if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId))) { // int bookmark = game.bookmarkState(); @@ -1113,7 +1115,7 @@ public abstract class PlayerImpl implements Player, Serializable { // what makes no real sense. So it makes no sense to generally do a restorState here. // restoreState(bookmark, card.getName(), game); } - // if the to play the land is replaced (e.g. Kjeldoran Outpos and don't sacrificing a Plains) it's a valid satte so returning true here + // if the to play the land is replaced (e.g. Kjeldoran Outpos and don't sacrificing a Plains) it's a valid state so returning true here return true; } @@ -3600,11 +3602,8 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean moveCardToExileWithInfo(Card card, UUID exileId, - String exileName, UUID sourceId, - Game game, Zone fromZone, - boolean withName - ) { + public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId, + Game game, Zone fromZone, boolean withName) { if (card == null) { return false; } diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 5ffce6bc344..f17930b11cd 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -33077,3 +33077,4 @@ Urza's Science Fair Project|Unglued|83|U|{6}|Artifact Creature - Construct|4|4| Snow Mercy|Happy Holidays|10|R|{2}{W}{W}|Snow Enchantment|||Whenever a creature deals damage to you, put a globe counter on it.${t},{untap},{t},{untap},{t}: Tap all creatures with globe counters on them.| Fruitcake Elemental|Happy Holidays|6|R|{1}{G}{G}|Creature - Elemental|7|7|Fruitcake Elemental is indestructible.$At the end of your turn, Fruitcake Elemental deals 7 damage to you.${3}: Target player gains control of Fruitcake Elemental.| Season's Beatings|Happy Holidays|9|R|{R}{R}{R}{R}|Sorcery|||Family gathering - Each creature target player controls deals damage equal to its power to another random creature that player controls.| +Mox Lotus|Unhinged|124|R|{15}|Artifact|||{t}: Add infinity to your mana pool.${100}: Add one mana of any color to your mana pool.&You don't lose life due to mana burn.| diff --git a/pom.xml b/pom.xml index 035656ceaf0..ce5b6b08bfb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.27 + 1.4.28 pom Mage Root Mage Root POM @@ -84,7 +84,7 @@ - 1.4.27 + 1.4.28 UTF-8 diff --git a/readme.md b/readme.md index 86fa502a958..131eda1e52b 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ [![Join the chat at https://gitter.im/magefree/mage](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/magefree/mage?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/magefree/mage.svg?branch=master)](https://travis-ci.org/magefree/mage) -XMage allows you to play Magic against one or more online players or computer opponents. It includes full rules enforcement for over **16 700** unique cards (over 32 000 counting all cards from different editions). Starting with *Morningtide*, all regular sets have nearly all the cards implemented ([detailed overview](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 **16950** unique cards (over 328000 counting all cards from different editions). Starting with *Morningtide*, all regular sets have nearly all the cards implemented. 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. @@ -28,7 +28,7 @@ XMage community: Download and install the [latest XMage release](http://XMage.de). You will need to have Version 7 or later of the [Java Runtime Environment](http://java.com/en/). -Look [here](http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=13632) for more detailed instructions. +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