From 9f470e2bd03ad28035e6f291bfc92fcd84e34af0 Mon Sep 17 00:00:00 2001 From: Kevin Shin Date: Thu, 23 Aug 2018 02:55:47 -0500 Subject: [PATCH 01/61] Fixes Flickerform to return the exiled creature under the creature's owner's control, instead of its own owner's control. #5084 --- Mage.Sets/src/mage/cards/f/Flickerform.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/f/Flickerform.java b/Mage.Sets/src/mage/cards/f/Flickerform.java index 550ae27ce61..b7aabe64753 100644 --- a/Mage.Sets/src/mage/cards/f/Flickerform.java +++ b/Mage.Sets/src/mage/cards/f/Flickerform.java @@ -156,8 +156,9 @@ class FlickerformReturnEffect extends OneShotEffect { } ExileZone exileZone = game.getExile().getExileZone(exileZoneId); Card enchantedCard = exileZone.get(enchantedCardId, game); + Player owner = game.getPlayer(enchantedCard.getOwnerId()); if (enchantedCard != null) { - controller.moveCards(enchantedCard, Zone.BATTLEFIELD, source, game); + owner.moveCards(enchantedCard, Zone.BATTLEFIELD, source, game); Permanent newPermanent = game.getPermanent(enchantedCardId); if (newPermanent != null) { Set toBattlefieldAttached = new HashSet(); From 97e4e2738b497b51d8147cdc0f354ea3e6dfa836 Mon Sep 17 00:00:00 2001 From: Kevin Shin Date: Sat, 25 Aug 2018 03:36:32 -0500 Subject: [PATCH 02/61] Added check for if the exiled card's owner is missing. --- Mage.Sets/src/mage/cards/f/Flickerform.java | 60 +++++++++++---------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/Mage.Sets/src/mage/cards/f/Flickerform.java b/Mage.Sets/src/mage/cards/f/Flickerform.java index b7aabe64753..0f0ef9da3e1 100644 --- a/Mage.Sets/src/mage/cards/f/Flickerform.java +++ b/Mage.Sets/src/mage/cards/f/Flickerform.java @@ -156,40 +156,44 @@ class FlickerformReturnEffect extends OneShotEffect { } ExileZone exileZone = game.getExile().getExileZone(exileZoneId); Card enchantedCard = exileZone.get(enchantedCardId, game); - Player owner = game.getPlayer(enchantedCard.getOwnerId()); + //skip if exiled card is missing if (enchantedCard != null) { - owner.moveCards(enchantedCard, Zone.BATTLEFIELD, source, game); - Permanent newPermanent = game.getPermanent(enchantedCardId); - if (newPermanent != null) { - Set toBattlefieldAttached = new HashSet(); - for (Card enchantment : exileZone.getCards(game)) { - if (filterAura.match(enchantment, game)) { - boolean canTarget = false; - for (Target target : enchantment.getSpellAbility().getTargets()) { - Filter filter = target.getFilter(); - if (filter.match(newPermanent, game)) { - canTarget = true; - break; + Player owner = game.getPlayer(enchantedCard.getOwnerId()); + //skip if card's owner is missing + if (owner != null) { + owner.moveCards(enchantedCard, Zone.BATTLEFIELD, source, game); + Permanent newPermanent = game.getPermanent(enchantedCardId); + if (newPermanent != null) { + Set toBattlefieldAttached = new HashSet(); + for (Card enchantment : exileZone.getCards(game)) { + if (filterAura.match(enchantment, game)) { + boolean canTarget = false; + for (Target target : enchantment.getSpellAbility().getTargets()) { + Filter filter = target.getFilter(); + if (filter.match(newPermanent, game)) { + canTarget = true; + break; + } + } + if (!canTarget) { + // Aura stays exiled + continue; + } + game.getState().setValue("attachTo:" + enchantment.getId(), newPermanent); + } + toBattlefieldAttached.add(enchantment); + } + if (!toBattlefieldAttached.isEmpty()) { + controller.moveCards(toBattlefieldAttached, Zone.BATTLEFIELD, source, game); + for (Card card : toBattlefieldAttached) { + if (game.getState().getZone(card.getId()) == Zone.BATTLEFIELD) { + newPermanent.addAttachment(card.getId(), game); } } - if (!canTarget) { - // Aura stays exiled - continue; - } - game.getState().setValue("attachTo:" + enchantment.getId(), newPermanent); - } - toBattlefieldAttached.add(enchantment); - } - if (!toBattlefieldAttached.isEmpty()) { - controller.moveCards(toBattlefieldAttached, Zone.BATTLEFIELD, source, game); - for (Card card : toBattlefieldAttached) { - if (game.getState().getZone(card.getId()) == Zone.BATTLEFIELD) { - newPermanent.addAttachment(card.getId(), game); - } } } + return true; } - return true; } return false; } From 81c2a62250868bb25f62ccdaef034fc2328ec59d Mon Sep 17 00:00:00 2001 From: Aaron Miller Date: Sat, 29 Sep 2018 19:14:39 -0700 Subject: [PATCH 03/61] Add "minimum rating" option to matches and tournaments --- .../mage/client/dialog/NewTableDialog.java | 34 ++++++++++++++----- .../client/dialog/NewTournamentDialog.java | 18 +++++++++- .../mage/client/dialog/PreferencesDialog.java | 2 ++ .../java/mage/client/table/TablesPanel.java | 14 +++++--- .../src/main/java/mage/view/TableView.java | 9 +++-- .../main/java/mage/server/MageServerImpl.java | 28 ++++++++++++++- .../java/mage/server/TableController.java | 30 ++++++++++++++++ .../java/mage/game/match/MatchOptions.java | 5 +++ .../game/tournament/TournamentOptions.java | 5 +++ 9 files changed, 126 insertions(+), 19 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index 0e3121f61d9..c47059ed798 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -51,6 +51,7 @@ public class NewTableDialog extends MageDialog { this.spnNumWins.setModel(new SpinnerNumberModel(1, 1, 5, 1)); this.spnFreeMulligans.setModel(new SpinnerNumberModel(0, 0, 5, 1)); this.spnQuitRatio.setModel(new SpinnerNumberModel(100, 0, 100, 5)); + this.spnMinimumRating.setModel(new SpinnerNumberModel(0, 0, 3000, 10)); this.spnEdhPowerLevel.setModel(new SpinnerNumberModel(100, 0, 100, 5)); MageFrame.getUI().addButton(MageComponents.NEW_TABLE_OK_BUTTON, btnOK); } @@ -102,8 +103,10 @@ public class NewTableDialog extends MageDialog { btnPreviousConfiguration2 = new javax.swing.JButton(); btnCancel = new javax.swing.JButton(); lblQuitRatio = new javax.swing.JLabel(); + lblMinimumRating = new javax.swing.JLabel(); lblEdhPowerLevel = new javax.swing.JLabel(); spnQuitRatio = new javax.swing.JSpinner(); + spnMinimumRating = new javax.swing.JSpinner(); spnEdhPowerLevel = new javax.swing.JSpinner(); setTitle("New Table"); @@ -186,9 +189,11 @@ public class NewTableDialog extends MageDialog { btnCancel.addActionListener(evt -> btnCancelActionPerformed(evt)); lblQuitRatio.setText("Allowed quit %"); + lblMinimumRating.setText("Minimum rating"); lblEdhPowerLevel.setText("EDH power level"); spnQuitRatio.setToolTipText("Players with quit % more than this value can't join this table"); + spnMinimumRating.setToolTipText("Players with rating less than this value can't join this table"); spnEdhPowerLevel.setToolTipText("Players with decks with a higher power level can't join this table"); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); @@ -239,9 +244,10 @@ public class NewTableDialog extends MageDialog { .addComponent(lblQuitRatio) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lblEdhPowerLevel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(lblMinimumRating) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(spnEdhPowerLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addComponent(spnMinimumRating, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)))) .addComponent(jLabel1, javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jLabel2, javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() @@ -270,7 +276,11 @@ public class NewTableDialog extends MageDialog { .addGap(18, 18, 18) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblNumWins) - .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lblEdhPowerLevel) + .addComponent(spnEdhPowerLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE))) .addComponent(jSeparator2) .addComponent(player1Panel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) @@ -297,14 +307,13 @@ public class NewTableDialog extends MageDialog { .addComponent(cbTimeLimit, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(cbDeckType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(lbDeckType) + .addComponent(cbDeckType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(chkRated) .addComponent(lblQuitRatio) - .addComponent(chkRated) .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lblEdhPowerLevel) - .addComponent(chkRated) - .addComponent(spnEdhPowerLevel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(lblMinimumRating) + .addComponent(spnMinimumRating, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) @@ -326,6 +335,7 @@ public class NewTableDialog extends MageDialog { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblSkillLevel) .addComponent(lblNumWins) + .addComponent(lblEdhPowerLevel) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(lblRange) .addComponent(lblAttack))) @@ -334,7 +344,8 @@ public class NewTableDialog extends MageDialog { .addComponent(cbRange, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(cbAttackOption, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(cbSkillLevel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(spnEdhPowerLevel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -394,6 +405,7 @@ public class NewTableDialog extends MageDialog { options.setFreeMulligans((Integer) this.spnFreeMulligans.getValue()); options.setPassword(this.txtPassword.getText()); options.setQuitRatio((Integer) this.spnQuitRatio.getValue()); + options.setMinimumRating((Integer) this.spnMinimumRating.getValue()); options.setEdhPowerLevel((Integer) this.spnEdhPowerLevel.getValue()); String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> ""); options.setBannedUsers(IgnoreList.ignoreList(serverAddress)); @@ -695,6 +707,7 @@ public class NewTableDialog extends MageDialog { } this.spnQuitRatio.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_QUIT_RATIO, "100"))); + this.spnMinimumRating.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_MINIMUM_RATING + versionStr, "0"))); this.spnEdhPowerLevel.setValue(0); } @@ -729,6 +742,7 @@ public class NewTableDialog extends MageDialog { PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_SPECTATORS_ALLOWED + versionStr, options.isSpectatorsAllowed() ? "Yes" : "No"); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PLANECHASE + versionStr, options.isPlaneChase() ? "Yes" : "No"); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_QUIT_RATIO + versionStr, Integer.toString(options.getQuitRatio())); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_MINIMUM_RATING + versionStr, Integer.toString(options.getMinimumRating())); StringBuilder playerTypesString = new StringBuilder(); for (Object player : players) { if (playerTypesString.length() > 0) { @@ -770,6 +784,7 @@ public class NewTableDialog extends MageDialog { private javax.swing.JLabel lblNumWins; private javax.swing.JLabel lblPassword; private javax.swing.JLabel lblQuitRatio; + private javax.swing.JLabel lblMinimumRating; private javax.swing.JLabel lblEdhPowerLevel; private javax.swing.JLabel lblRange; private javax.swing.JLabel lblSkillLevel; @@ -779,6 +794,7 @@ public class NewTableDialog extends MageDialog { private javax.swing.JSpinner spnNumPlayers; private javax.swing.JSpinner spnNumWins; private javax.swing.JSpinner spnQuitRatio; + private javax.swing.JSpinner spnMinimumRating; private javax.swing.JSpinner spnEdhPowerLevel; private javax.swing.JTextField txtName; private javax.swing.JTextField txtPassword; diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java index f5f2961f513..e11e0c62767 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java @@ -74,6 +74,7 @@ public class NewTournamentDialog extends MageDialog { this.spnConstructTime.setModel(new SpinnerNumberModel(10, CONSTRUCTION_TIME_MIN, CONSTRUCTION_TIME_MAX, 2)); this.spnNumRounds.setModel(new SpinnerNumberModel(2, 2, 10, 1)); this.spnQuitRatio.setModel(new SpinnerNumberModel(100, 0, 100, 5)); + this.spnMinimumRating.setModel(new SpinnerNumberModel(0, 0, 3000, 10)); } public void showDialog(UUID roomId) { @@ -164,6 +165,8 @@ public class NewTournamentDialog extends MageDialog { pnlRandomPacks = new javax.swing.JPanel(); lblQuitRatio = new javax.swing.JLabel(); spnQuitRatio = new javax.swing.JSpinner(); + lblMinimumRating = new javax.swing.JLabel(); + spnMinimumRating = new javax.swing.JSpinner(); setTitle("New Tournament"); @@ -314,8 +317,10 @@ public class NewTournamentDialog extends MageDialog { pnlRandomPacks.setLayout(new javax.swing.BoxLayout(pnlRandomPacks, javax.swing.BoxLayout.Y_AXIS)); lblQuitRatio.setText("Allowed quit %:"); + lblMinimumRating.setText("Minimum rating:"); spnQuitRatio.setToolTipText("Players with quit % more than this value can't join this table"); + spnMinimumRating.setToolTipText("Players with rating less than this value can't join this table"); spnNumSeats.setToolTipText("The number of seats for each duel. If more than 2, will set number of wins to 1"); spnNumPlayers.setToolTipText("The total number of players who will draft"); @@ -385,11 +390,15 @@ public class NewTournamentDialog extends MageDialog { .addComponent(lblNumWins) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(lblQuitRatio) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(lblMinimumRating) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(spnMinimumRating, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(chkRated)) .addComponent(cbTournamentType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE))) .addGroup(layout.createSequentialGroup() @@ -443,6 +452,8 @@ public class NewTournamentDialog extends MageDialog { .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(lblQuitRatio) .addComponent(spnQuitRatio, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lblMinimumRating) + .addComponent(spnMinimumRating, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(chkRated)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) @@ -521,6 +532,7 @@ public class NewTournamentDialog extends MageDialog { tOptions.setWatchingAllowed(cbAllowSpectators.isSelected()); tOptions.setPlaneChase(cbPlaneChase.isSelected()); tOptions.setQuitRatio((Integer) spnQuitRatio.getValue()); + tOptions.setMinimumRating((Integer) spnMinimumRating.getValue()); for (TournamentPlayerPanel player : players) { tOptions.getPlayerTypes().add((PlayerType) player.getPlayerType().getSelectedItem()); } @@ -1061,6 +1073,7 @@ public class NewTournamentDialog extends MageDialog { this.spnFreeMulligans.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_OF_FREE_MULLIGANS + versionStr, "0"))); this.spnNumWins.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_OF_WINS + versionStr, "2"))); this.spnQuitRatio.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_QUIT_RATIO + versionStr, "100"))); + this.spnMinimumRating.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_MINIMUM_RATING + versionStr, "0"))); TournamentTypeView tournamentType = (TournamentTypeView) cbTournamentType.getSelectedItem(); activatePanelElements(tournamentType); @@ -1146,6 +1159,7 @@ public class NewTournamentDialog extends MageDialog { PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_OF_FREE_MULLIGANS + versionStr, Integer.toString(tOptions.getMatchOptions().getFreeMulligans())); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_OF_WINS + versionStr, Integer.toString(tOptions.getMatchOptions().getWinsNeeded())); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_QUIT_RATIO + versionStr, Integer.toString(tOptions.getQuitRatio())); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_MINIMUM_RATING + versionStr, Integer.toString(tOptions.getMinimumRating())); if (tOptions.getTournamentType().startsWith("Sealed")) { PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PACKS_SEALED + versionStr, tOptions.getLimitedOptions().getSetCodes().toString()); @@ -1217,6 +1231,7 @@ public class NewTournamentDialog extends MageDialog { private javax.swing.JLabel lblPassword; private javax.swing.JLabel lblPlayer1; private javax.swing.JLabel lblQuitRatio; + private javax.swing.JLabel lblMinimumRating; private javax.swing.JLabel lblTournamentType; private mage.client.table.NewPlayerPanel player1Panel; private javax.swing.JPanel pnlDraftOptions; @@ -1231,6 +1246,7 @@ public class NewTournamentDialog extends MageDialog { private javax.swing.JSpinner spnNumRounds; private javax.swing.JSpinner spnNumWins; private javax.swing.JSpinner spnQuitRatio; + private javax.swing.JSpinner spnMinimumRating; private javax.swing.JTextField txtName; private javax.swing.JTextField txtPassword; private org.jdesktop.beansbinding.BindingGroup bindingGroup; 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 4d595a005a8..5826d2c85b3 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -222,6 +222,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_NEW_TABLE_NUMBER_PLAYERS = "newTableNumberPlayers"; public static final String KEY_NEW_TABLE_PLAYER_TYPES = "newTablePlayerTypes"; public static final String KEY_NEW_TABLE_QUIT_RATIO = "newTableQuitRatio"; + public static final String KEY_NEW_TABLE_MINIMUM_RATING = "newTableMinimumRating"; public static final String KEY_NEW_TABLE_RATED = "newTableRated"; // pref setting for new tournament dialog @@ -243,6 +244,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_NEW_TOURNAMENT_ALLOW_ROLLBACKS = "newTournamentAllowRollbacks"; public static final String KEY_NEW_TOURNAMENT_DECK_FILE = "newTournamentDeckFile"; public static final String KEY_NEW_TOURNAMENT_QUIT_RATIO = "newTournamentQuitRatio"; + public static final String KEY_NEW_TOURNAMENT_MINIMUM_RATING = "newTournamentMinimumRating"; public static final String KEY_NEW_TOURNAMENT_RATED = "newTournamentRated"; // pref setting for deck generator 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 d28badf2f79..e486c5f8343 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -1221,6 +1221,7 @@ public class TablesPanel extends javax.swing.JPanel { options.setSkillLevel(SkillLevel.CASUAL); options.setRollbackTurnsAllowed(true); options.setQuitRatio(100); + options.setMinimumRating(0); String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> ""); options.setBannedUsers(IgnoreList.ignoreList(serverAddress)); table = SessionHandler.createTable(roomId, options); @@ -1335,14 +1336,15 @@ class TableTableModel extends AbstractTableModel { public static final int COLUMN_SKILL = 8; public static final int COLUMN_RATING = 9; public static final int COLUMN_QUIT_RATIO = 10; - public static final int ACTION_COLUMN = 11; // column the action is located (starting with 0) + public static final int COLUMN_MINIMUM_RATING = 11; + public static final int ACTION_COLUMN = 12; // column the action is located (starting with 0) public static final String RATED_VALUE_YES = "YES"; public static final String RATED_VALUE_NO = ""; public static final String PASSWORD_VALUE_YES = "YES"; - private final String[] columnNames = new String[]{"M/T", "Deck Type", "Owner / Players", "Game Type", "Info", "Status", "Password", "Created / Started", "Skill Level", "Rating", "Quit %", "Action"}; + private final String[] columnNames = new String[]{"M/T", "Deck Type", "Owner / Players", "Game Type", "Info", "Status", "Password", "Created / Started", "Skill Level", "Rated", "Quit %", "Min Rating", "Action"}; private TableView[] tables = new TableView[0]; @@ -1390,6 +1392,8 @@ class TableTableModel extends AbstractTableModel { case 10: return tables[arg0].getQuitRatio(); case 11: + return tables[arg0].getMinimumRating(); + case 12: switch (tables[arg0].getTableState()) { case WAITING: @@ -1419,14 +1423,14 @@ class TableTableModel extends AbstractTableModel { default: return ""; } - case 12: - return tables[arg0].isTournament(); case 13: + return tables[arg0].isTournament(); + case 14: if (!tables[arg0].getGames().isEmpty()) { return tables[arg0].getGames().get(0); } return null; - case 14: + case 15: return tables[arg0].getTableId(); } return ""; diff --git a/Mage.Common/src/main/java/mage/view/TableView.java b/Mage.Common/src/main/java/mage/view/TableView.java index c3d748c3446..948c7717bf1 100644 --- a/Mage.Common/src/main/java/mage/view/TableView.java +++ b/Mage.Common/src/main/java/mage/view/TableView.java @@ -36,6 +36,7 @@ public class TableView implements Serializable { private List seats = new ArrayList<>(); private List games = new ArrayList<>(); private final String quitRatio; + private final String minimumRating; private final boolean limited; private final boolean rated; private final boolean passworded; @@ -111,6 +112,7 @@ public class TableView implements Serializable { this.additionalInfo = addInfo.toString(); this.skillLevel = table.getMatch().getOptions().getSkillLevel(); this.quitRatio = Integer.toString(table.getMatch().getOptions().getQuitRatio()); + this.minimumRating = Integer.toString(table.getMatch().getOptions().getMinimumRating()); this.limited = table.getMatch().getOptions().isLimited(); this.rated = table.getMatch().getOptions().isRated(); this.passworded = !table.getMatch().getOptions().getPassword().isEmpty(); @@ -159,6 +161,7 @@ public class TableView implements Serializable { this.deckType = table.getDeckType() + ' ' + table.getTournament().getBoosterInfo() + (tableNameInfo != null ? tableNameInfo : ""); this.skillLevel = table.getTournament().getOptions().getMatchOptions().getSkillLevel(); this.quitRatio = Integer.toString(table.getTournament().getOptions().getQuitRatio()); + this.minimumRating = Integer.toString(table.getTournament().getOptions().getMinimumRating()); this.limited = table.getTournament().getOptions().getMatchOptions().isLimited(); this.rated = table.getTournament().getOptions().getMatchOptions().isRated(); this.passworded = !table.getTournament().getOptions().getPassword().isEmpty(); @@ -223,9 +226,9 @@ public class TableView implements Serializable { return skillLevel; } - public String getQuitRatio() { - return quitRatio; - } + public String getQuitRatio() { return quitRatio; } + + public String getMinimumRating() { return minimumRating; } public boolean isLimited() { return limited; diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index f6676754ef3..35e7fb4e907 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -227,6 +227,20 @@ public class MageServerImpl implements MageServer { user.showUserMessage("Create tournament", message); throw new MageException("No message"); } + // check if the user satisfies the minimumRating requirement. + int minimumRating = options.getMinimumRating(); + int userRating; + if (options.getMatchOptions().isLimited()) { + userRating = user.getUserData().getLimitedRating(); + } else { + userRating = user.getUserData().getConstructedRating(); + } + if (userRating < minimumRating) { + String message = new StringBuilder("Your rating ").append(userRating) + .append(" is lower than the table requirement ").append(minimumRating).toString(); + user.showUserMessage("Create tournament", message); + throw new MageException("No message"); + } Optional room = GamesRoomManager.instance.getRoom(roomId); if (!room.isPresent()) { @@ -1386,7 +1400,19 @@ public class MageServerImpl implements MageServer { user.showUserMessage("Create table", "Your quit ratio " + user.getMatchQuitRatio() + "% is higher than the table requirement " + quitRatio + '%'); throw new MageException("No message"); } - + // check if the user satisfies the minimumRating requirement. + int minimumRating = options.getMinimumRating(); + int userRating; + if (options.isLimited()) { + userRating = user.getUserData().getLimitedRating(); + } else { + userRating = user.getUserData().getConstructedRating(); + } + if (userRating < minimumRating) { + String message = new StringBuilder("Your rating ").append(userRating).append(" is lower than the table requirement ").append(minimumRating).toString(); + user.showUserMessage("Create table", message); + throw new MageException("No message"); + } Optional room = GamesRoomManager.instance.getRoom(roomId); if (room.isPresent()) { TableView table = room.get().createTable(userId, options); diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index 58a0b695b85..1d511d7e4f8 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -172,6 +172,21 @@ public class TableController { return false; } + // Check minimum rating. + int minimumRating = table.getTournament().getOptions().getMinimumRating(); + int userRating; + if (table.getTournament().getOptions().getMatchOptions().isLimited()) { + userRating = user.getUserData().getLimitedRating(); + } else { + userRating = user.getUserData().getConstructedRating(); + } + if (userRating < minimumRating) { + String message = new StringBuilder("Your rating ").append(userRating) + .append(" is lower than the table requirement ").append(minimumRating).toString(); + user.showUserMessage("Join Table", message); + return false; + } + Optional playerOptional = createPlayer(name, seat.getPlayerType(), skill); if (playerOptional.isPresent()) { Player player = playerOptional.get(); @@ -272,6 +287,21 @@ public class TableController { return false; } + // Check minimum rating. + int minimumRating = table.getMatch().getOptions().getMinimumRating(); + int userRating; + if (table.getMatch().getOptions().isLimited()) { + userRating = user.getUserData().getLimitedRating(); + } else { + userRating = user.getUserData().getConstructedRating(); + } + if (userRating < minimumRating) { + String message = new StringBuilder("Your rating ").append(userRating) + .append(" is lower than the table requirement ").append(minimumRating).toString(); + user.showUserMessage("Join Table", message); + return false; + } + // Check power level for table (currently only used for EDH/Commander table) int edhPowerLevel = table.getMatch().getOptions().getEdhPowerLevel(); if (edhPowerLevel > 0 && table.getValidator().getName().toLowerCase(Locale.ENGLISH).equals("commander")) { diff --git a/Mage/src/main/java/mage/game/match/MatchOptions.java b/Mage/src/main/java/mage/game/match/MatchOptions.java index 02e6808416c..47ad873e631 100644 --- a/Mage/src/main/java/mage/game/match/MatchOptions.java +++ b/Mage/src/main/java/mage/game/match/MatchOptions.java @@ -37,6 +37,7 @@ public class MatchOptions implements Serializable { protected boolean spectatorsAllowed; protected boolean planeChase; protected int quitRatio; + protected int minimumRating; protected int edhPowerLevel; protected boolean rated; protected int numSeatsForMatch; @@ -205,6 +206,10 @@ public class MatchOptions implements Serializable { this.quitRatio = quitRatio; } + public int getMinimumRating() { return minimumRating; } + + public void setMinimumRating(int minimumRating) { this.minimumRating = minimumRating; } + public int getEdhPowerLevel() { return edhPowerLevel; } diff --git a/Mage/src/main/java/mage/game/tournament/TournamentOptions.java b/Mage/src/main/java/mage/game/tournament/TournamentOptions.java index 2b042d19a1d..c81294ce3ee 100644 --- a/Mage/src/main/java/mage/game/tournament/TournamentOptions.java +++ b/Mage/src/main/java/mage/game/tournament/TournamentOptions.java @@ -24,6 +24,7 @@ public class TournamentOptions implements Serializable { protected int numberRounds; protected String password; protected int quitRatio; + protected int minimumRating; public TournamentOptions(String name, String matchType, int numSeats) { this.name = name; @@ -98,4 +99,8 @@ public class TournamentOptions implements Serializable { public void setQuitRatio(int quitRatio) { this.quitRatio = quitRatio; } + + public int getMinimumRating() { return minimumRating; } + + public void setMinimumRating(int minimumRating) { this.minimumRating = minimumRating; } } From 88f3db03c4ac3e575e51c3f3be8654bd78edf4c9 Mon Sep 17 00:00:00 2001 From: Tyler Moore Date: Wed, 3 Oct 2018 17:38:30 -0700 Subject: [PATCH 04/61] Both players' decks show up at the same time, but hangs if you don't select in the correct order --- .../mage/cards/j/JaceArchitectOfThought.java | 68 +++++++++++++++---- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index 09ebb7644ef..1caa13f393f 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -1,6 +1,8 @@ package mage.cards.j; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Set; import java.util.UUID; @@ -34,10 +36,13 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInExile; import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetOpponent; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; +import javax.swing.*; + /** * * @@ -239,23 +244,21 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { if (controller == null || sourcePermanent == null) { return false; } + ArrayList threads = new ArrayList<>(); for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - String playerName = new StringBuilder(player.getLogName()).append("'s").toString(); - if (source.isControlledBy(player.getId())) { - playerName = "your"; - } - TargetCardInLibrary target = new TargetCardInLibrary(new FilterNonlandCard(new StringBuilder("nonland card from ").append(playerName).append(" library").toString())); - if (controller.searchLibrary(target, game, playerId)) { - UUID targetId = target.getFirstTarget(); - Card card = player.getLibrary().remove(targetId, game); - if (card != null) { - controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true); + JaceArchitectOfThoughtEffect3Helper thread = new JaceArchitectOfThoughtEffect3Helper(playerId, game, source, sourcePermanent, controller); + thread.execute(); + thread.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if("state".equals(evt.getPropertyName()) && SwingWorker.StateValue.DONE.equals(evt.getNewValue())){ + threads.remove(evt.getSource()); + } } - } - player.shuffleLibrary(source, game); + }); + threads.add(thread); } - + while(threads.size() > 0) {} ExileZone jaceExileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); if (jaceExileZone == null) { return true; @@ -274,3 +277,40 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { return true; } } + +class JaceArchitectOfThoughtEffect3Helper extends SwingWorker { + + UUID playerId; + Game game; + Ability source; + Permanent sourcePermanent; + Player controller; + + public JaceArchitectOfThoughtEffect3Helper (UUID playerId, Game game, Ability source, Permanent sourcePermanent, Player controller) { + this.playerId = playerId; + this.game = game; + this.source = source; + this.sourcePermanent = sourcePermanent; + this.controller = controller; + } + + @Override + protected Boolean doInBackground() throws Exception { + Player player = this.game.getPlayer(this.playerId); + String playerName = new StringBuilder(player.getLogName()).append("'s").toString(); + if (this.source.isControlledBy(player.getId())) { + playerName = "your"; + } + TargetCardInLibrary target = new TargetCardInLibrary(new FilterNonlandCard(new StringBuilder("nonland card from ").append(playerName).append(" library").toString())); + if (this.controller.searchLibrary(target, this.game, this.playerId)) { + UUID targetId = target.getFirstTarget(); + Card card = player.getLibrary().remove(targetId, this.game); + if (card != null) { + this.controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(this.game, this.source), this.sourcePermanent.getIdName(), this.source.getSourceId(), this.game, Zone.LIBRARY, true); + } + player.shuffleLibrary(this.source, this.game); + } + return true; + } + +} From 02d9287cfae35c50956ec2b81971fc32ede28956 Mon Sep 17 00:00:00 2001 From: Tyler Moore Date: Thu, 4 Oct 2018 11:19:40 -0700 Subject: [PATCH 05/61] Reverting concurrent code to start fresh --- .../mage/cards/j/JaceArchitectOfThought.java | 68 ++++--------------- 1 file changed, 14 insertions(+), 54 deletions(-) diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index 1caa13f393f..09ebb7644ef 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -1,8 +1,6 @@ package mage.cards.j; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Set; import java.util.UUID; @@ -36,13 +34,10 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInExile; import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetOpponent; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; -import javax.swing.*; - /** * * @@ -244,21 +239,23 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { if (controller == null || sourcePermanent == null) { return false; } - ArrayList threads = new ArrayList<>(); for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - JaceArchitectOfThoughtEffect3Helper thread = new JaceArchitectOfThoughtEffect3Helper(playerId, game, source, sourcePermanent, controller); - thread.execute(); - thread.addPropertyChangeListener(new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if("state".equals(evt.getPropertyName()) && SwingWorker.StateValue.DONE.equals(evt.getNewValue())){ - threads.remove(evt.getSource()); - } + Player player = game.getPlayer(playerId); + String playerName = new StringBuilder(player.getLogName()).append("'s").toString(); + if (source.isControlledBy(player.getId())) { + playerName = "your"; + } + TargetCardInLibrary target = new TargetCardInLibrary(new FilterNonlandCard(new StringBuilder("nonland card from ").append(playerName).append(" library").toString())); + if (controller.searchLibrary(target, game, playerId)) { + UUID targetId = target.getFirstTarget(); + Card card = player.getLibrary().remove(targetId, game); + if (card != null) { + controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true); } - }); - threads.add(thread); + } + player.shuffleLibrary(source, game); } - while(threads.size() > 0) {} + ExileZone jaceExileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); if (jaceExileZone == null) { return true; @@ -277,40 +274,3 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { return true; } } - -class JaceArchitectOfThoughtEffect3Helper extends SwingWorker { - - UUID playerId; - Game game; - Ability source; - Permanent sourcePermanent; - Player controller; - - public JaceArchitectOfThoughtEffect3Helper (UUID playerId, Game game, Ability source, Permanent sourcePermanent, Player controller) { - this.playerId = playerId; - this.game = game; - this.source = source; - this.sourcePermanent = sourcePermanent; - this.controller = controller; - } - - @Override - protected Boolean doInBackground() throws Exception { - Player player = this.game.getPlayer(this.playerId); - String playerName = new StringBuilder(player.getLogName()).append("'s").toString(); - if (this.source.isControlledBy(player.getId())) { - playerName = "your"; - } - TargetCardInLibrary target = new TargetCardInLibrary(new FilterNonlandCard(new StringBuilder("nonland card from ").append(playerName).append(" library").toString())); - if (this.controller.searchLibrary(target, this.game, this.playerId)) { - UUID targetId = target.getFirstTarget(); - Card card = player.getLibrary().remove(targetId, this.game); - if (card != null) { - this.controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(this.game, this.source), this.sourcePermanent.getIdName(), this.source.getSourceId(), this.game, Zone.LIBRARY, true); - } - player.shuffleLibrary(this.source, this.game); - } - return true; - } - -} From c5cedc2abacbe2fff2e2564d8663244824df8e78 Mon Sep 17 00:00:00 2001 From: Tyler Moore Date: Thu, 4 Oct 2018 13:45:44 -0700 Subject: [PATCH 06/61] Adding lookAtAllLibraries to Player/PlayerImpl and using that method during JAOT's -8 resolution --- .../src/mage/cards/j/JaceArchitectOfThought.java | 2 ++ Mage/src/main/java/mage/players/Player.java | 9 +++++++++ Mage/src/main/java/mage/players/PlayerImpl.java | 12 ++++++++++++ 3 files changed, 23 insertions(+) diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index 09ebb7644ef..8a5c9342e1d 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -239,12 +239,14 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { if (controller == null || sourcePermanent == null) { return false; } + controller.lookAtAllLibraries(source, game); for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); String playerName = new StringBuilder(player.getLogName()).append("'s").toString(); if (source.isControlledBy(player.getId())) { playerName = "your"; } + TargetCardInLibrary target = new TargetCardInLibrary(new FilterNonlandCard(new StringBuilder("nonland card from ").append(playerName).append(" library").toString())); if (controller.searchLibrary(target, game, playerId)) { UUID targetId = target.getFirstTarget(); diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index a730164f736..f6f30cf08e5 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -350,6 +350,15 @@ public interface Player extends MageItem, Copyable { */ boolean searchLibrary(TargetCardInLibrary target, Game game, UUID targetPlayerId, boolean triggerEvents); + /** + * Reveals all players' libraries. Useful for abilities like Jace, Architect of Thought's -8 + * that have effects that require information from all libraries. + * @param source + * @param game + * @return + */ + boolean lookAtAllLibraries(Ability source, Game game); + boolean canPlayLand(); /** diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 2659dfd1450..9b202e4466c 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2477,6 +2477,18 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } + @Override + public boolean lookAtAllLibraries(Ability source, Game game) { + for(UUID playerId : game.getState().getPlayersInRange(this.getId(), game)){ + Player player = game.getPlayer(playerId); + String playerName = this.getName().equals(player.getName()) ? "Your " : player.getName() + "'s "; + playerName += "library"; + Cards cardsInLibrary = new CardsImpl(player.getLibrary().getTopCards(game, player.getLibrary().size())); + lookAtCards(playerName, cardsInLibrary, game); + } + return true; + } + private boolean handleLibraryCastableCards(Library library, Game game, UUID targetPlayerId) { // for handling Panglacial Wurm boolean alreadyChosenUse = false; From 4057cc2859b0e448b72fd6845daa529ec0ff3a0b Mon Sep 17 00:00:00 2001 From: Tyler Moore Date: Thu, 4 Oct 2018 14:16:52 -0700 Subject: [PATCH 07/61] Implementing new function in all classes implementing Player --- Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java | 4 ++++ Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java | 3 +++ Mage/src/main/java/mage/players/Player.java | 2 +- Mage/src/main/java/mage/players/PlayerImpl.java | 3 +-- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 0fa4a84a094..05837e862f4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -2072,6 +2072,10 @@ public class TestPlayer implements Player { return computerPlayer.searchLibrary(target, game, targetPlayerId, triggerEvents); } + public void lookAtAllLibraries(Ability source, Game game) { + computerPlayer.lookAtAllLibraries(source, game); + } + @Override public boolean flipCoin(Game game) { return computerPlayer.flipCoin(game); diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java index 810c068ccf6..360bd942514 100644 --- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java +++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java @@ -597,6 +597,9 @@ public class PlayerStub implements Player { return false; } + @Override + public void lookAtAllLibraries(Ability source, Game game) {} + @Override public boolean canPlayLand() { return false; diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index f6f30cf08e5..ec86ad550c1 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -357,7 +357,7 @@ public interface Player extends MageItem, Copyable { * @param game * @return */ - boolean lookAtAllLibraries(Ability source, Game game); + void lookAtAllLibraries(Ability source, Game game); boolean canPlayLand(); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 9b202e4466c..53053fb0466 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2478,7 +2478,7 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean lookAtAllLibraries(Ability source, Game game) { + public void lookAtAllLibraries(Ability source, Game game) { for(UUID playerId : game.getState().getPlayersInRange(this.getId(), game)){ Player player = game.getPlayer(playerId); String playerName = this.getName().equals(player.getName()) ? "Your " : player.getName() + "'s "; @@ -2486,7 +2486,6 @@ public abstract class PlayerImpl implements Player, Serializable { Cards cardsInLibrary = new CardsImpl(player.getLibrary().getTopCards(game, player.getLibrary().size())); lookAtCards(playerName, cardsInLibrary, game); } - return true; } private boolean handleLibraryCastableCards(Library library, Game game, UUID targetPlayerId) { From 297b543630a6ea301234a787e472baed6f73047b Mon Sep 17 00:00:00 2001 From: L_J Date: Thu, 4 Oct 2018 22:12:06 +0000 Subject: [PATCH 08/61] Jace, Architect of Thought ult fix --- .../mage/cards/j/JaceArchitectOfThought.java | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index 09ebb7644ef..e6c207f4d5a 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -2,6 +2,7 @@ package mage.cards.j; import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.UUID; import mage.MageObjectReference; @@ -24,7 +25,10 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.FilterPlayer; import mage.filter.common.FilterNonlandCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.other.PlayerIdPredicate; import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; @@ -32,6 +36,7 @@ import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; +import mage.target.TargetPlayer; import mage.target.common.TargetCardInExile; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetOpponent; @@ -239,21 +244,49 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { if (controller == null || sourcePermanent == null) { return false; } - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - String playerName = new StringBuilder(player.getLogName()).append("'s").toString(); - if (source.isControlledBy(player.getId())) { - playerName = "your"; + List playerList = new ArrayList<>(); + playerList.addAll(game.getState().getPlayersInRange(controller.getId(), game)); + List checkList = new ArrayList<>(); + while (!playerList.isEmpty()) { + FilterPlayer filter = new FilterPlayer(); + List playerPredicates = new ArrayList<>(); + for (UUID playerId : playerList) { + playerPredicates.add(new PlayerIdPredicate(playerId)); } - TargetCardInLibrary target = new TargetCardInLibrary(new FilterNonlandCard(new StringBuilder("nonland card from ").append(playerName).append(" library").toString())); - if (controller.searchLibrary(target, game, playerId)) { - UUID targetId = target.getFirstTarget(); - Card card = player.getLibrary().remove(targetId, game); - if (card != null) { - controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true); + filter.add(Predicates.or(playerPredicates)); + TargetPlayer targetPlayer = new TargetPlayer(1, 1, true, filter); + targetPlayer.setRequired(!checkList.containsAll(playerList)); + if (controller.chooseTarget(outcome, targetPlayer, source, game)) { + UUID playerId = targetPlayer.getFirstTarget(); + Player player = game.getPlayer(playerId); + if (player != null) { + String playerName = new StringBuilder(player.getLogName()).append("'s").toString(); + if (source.isControlledBy(player.getId())) { + playerName = "your"; + } + TargetCardInLibrary target = new TargetCardInLibrary(new FilterNonlandCard(new StringBuilder("nonland card from ").append(playerName).append(" library").toString())); + if (controller.searchLibrary(target, game, playerId)) { + UUID targetId = target.getFirstTarget(); + Card card = player.getLibrary().remove(targetId, game); + if (card != null) { + controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true); + playerList.remove(playerId); + } + } + if (!checkList.contains(playerId)) { + player.shuffleLibrary(source, game); // only one shuffle per library (to prevent bad interactions with Psychogenic Probe etc.) + } + } + checkList.add(playerId); + } else { + break; + } + for (UUID playerId : playerList) { + Player player = game.getPlayer(playerId); + if (player == null || !player.canRespond()) { + playerList.remove(player); } } - player.shuffleLibrary(source, game); } ExileZone jaceExileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); From 2ed63a7184cc29bb163ad17fb702a1f28395d915 Mon Sep 17 00:00:00 2001 From: Tyler Moore Date: Thu, 4 Oct 2018 15:14:43 -0700 Subject: [PATCH 09/61] Notifying players of library search, and giving controller choice to search --- Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java | 5 ++++- .../src/test/java/org/mage/test/player/TestPlayer.java | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index 8a5c9342e1d..125d144b15d 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -239,7 +239,10 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { if (controller == null || sourcePermanent == null) { return false; } - controller.lookAtAllLibraries(source, game); + if(controller.chooseUse(Outcome.Benefit, "Look at all players' libraries before card select?", null, game)) { + game.informPlayers(controller.getLogName() + " is looking at all players' libraries."); + controller.lookAtAllLibraries(source, game); + } for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); String playerName = new StringBuilder(player.getLogName()).append("'s").toString(); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 05837e862f4..8c22e36169e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -2072,6 +2072,7 @@ public class TestPlayer implements Player { return computerPlayer.searchLibrary(target, game, targetPlayerId, triggerEvents); } + @Override public void lookAtAllLibraries(Ability source, Game game) { computerPlayer.lookAtAllLibraries(source, game); } From 57fdae9c9235ce115e8b5d37fec99a82e892890d Mon Sep 17 00:00:00 2001 From: L_J Date: Thu, 4 Oct 2018 22:43:23 +0000 Subject: [PATCH 10/61] Minor shuffle fix --- Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index e6c207f4d5a..770ae965aae 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -273,9 +273,6 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { playerList.remove(playerId); } } - if (!checkList.contains(playerId)) { - player.shuffleLibrary(source, game); // only one shuffle per library (to prevent bad interactions with Psychogenic Probe etc.) - } } checkList.add(playerId); } else { @@ -288,7 +285,12 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { } } } - + for (UUID playerId : checkList) { + Player player = game.getPlayer(playerId); + if (player != null) { + player.shuffleLibrary(source, game); + } + } ExileZone jaceExileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); if (jaceExileZone == null) { return true; From bf4988382ed17befe1e3de59f6f8bdec8a67987c Mon Sep 17 00:00:00 2001 From: L_J Date: Thu, 4 Oct 2018 22:53:50 +0000 Subject: [PATCH 11/61] More edits --- Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index 770ae965aae..88695547985 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -266,15 +266,19 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { } TargetCardInLibrary target = new TargetCardInLibrary(new FilterNonlandCard(new StringBuilder("nonland card from ").append(playerName).append(" library").toString())); if (controller.searchLibrary(target, game, playerId)) { + checkList.add(playerId); UUID targetId = target.getFirstTarget(); Card card = player.getLibrary().remove(targetId, game); if (card != null) { controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true); playerList.remove(playerId); } + } else { + playerList.remove(playerId); } + } else { + playerList.remove(playerId); } - checkList.add(playerId); } else { break; } From b67107b1b5de09bc0dd0e4c402f39fb782daaaf5 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 10 Oct 2018 22:55:42 +0000 Subject: [PATCH 12/61] Prevent arbitrary library search triggers from Jace ultimate --- Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index 88695547985..2dd15baf2d1 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -265,7 +265,7 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { playerName = "your"; } TargetCardInLibrary target = new TargetCardInLibrary(new FilterNonlandCard(new StringBuilder("nonland card from ").append(playerName).append(" library").toString())); - if (controller.searchLibrary(target, game, playerId)) { + if (controller.searchLibrary(target, game, playerId, !checkList.contains(playerId))) { checkList.add(playerId); UUID targetId = target.getFirstTarget(); Card card = player.getLibrary().remove(targetId, game); From 9939af89683b6a9cad560899084dcbe8e0ae6322 Mon Sep 17 00:00:00 2001 From: L_J Date: Wed, 10 Oct 2018 23:09:49 +0000 Subject: [PATCH 13/61] Prevent arbitrary shuffles from Jace ultimate --- Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index 2dd15baf2d1..f5f54108041 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -2,6 +2,7 @@ package mage.cards.j; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; @@ -246,7 +247,7 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { } List playerList = new ArrayList<>(); playerList.addAll(game.getState().getPlayersInRange(controller.getId(), game)); - List checkList = new ArrayList<>(); + Set checkList = new HashSet<>(); while (!playerList.isEmpty()) { FilterPlayer filter = new FilterPlayer(); List playerPredicates = new ArrayList<>(); From baeb9d8b0089d733cea0b941a02e9aa6e6707476 Mon Sep 17 00:00:00 2001 From: John Hitchings Date: Thu, 8 Nov 2018 09:25:03 -0800 Subject: [PATCH 14/61] Prepopulate DeckImportFromClipboardDialog content with string contents of clipboard. --- .../deckeditor/DeckImportFromClipboardDialog.java | 15 +++++++++++++++ .../cards/decks/importer/DeckImporterUtil.java | 4 +++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportFromClipboardDialog.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportFromClipboardDialog.java index eef1987dfaa..6c890632173 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportFromClipboardDialog.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportFromClipboardDialog.java @@ -3,11 +3,15 @@ package mage.client.deckeditor; import mage.util.StreamUtils; import java.awt.*; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.*; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.util.Optional; + import javax.swing.*; public class DeckImportFromClipboardDialog extends JDialog { @@ -38,6 +42,17 @@ public class DeckImportFromClipboardDialog extends JDialog { // Close on "ESC" contentPane.registerKeyboardAction(e -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + + getClipboardStringData().ifPresent(content -> txtDeckList.setText(content)); + } + + private Optional getClipboardStringData() { + try { + return Optional.of((String)Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor)); + } catch (HeadlessException | UnsupportedFlavorException | IOException e) { + e.printStackTrace(); + } + return Optional.empty(); } private void onOK() { diff --git a/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java b/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java index a0e90dc5261..0384a17f44c 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java @@ -39,7 +39,9 @@ public final class DeckImporterUtil { } public static DeckImporter getDeckImporter(String file) { - if (file.toLowerCase(Locale.ENGLISH).endsWith("dec")) { + if (file == null) { + return null; + } if (file.toLowerCase(Locale.ENGLISH).endsWith("dec")) { return new DecDeckImporter(); } else if (file.toLowerCase(Locale.ENGLISH).endsWith("mwdeck")) { return new MWSDeckImporter(); From ed7c32f9e8668d93f90d8a6a0267445fd3312fe2 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 16 Nov 2018 16:14:10 -0600 Subject: [PATCH 15/61] - Added Scrying Glass and Metathrane Elite. --- .../src/mage/cards/m/MetathranElite.java | 47 +++ Mage.Sets/src/mage/cards/s/ScryingGlass.java | 87 +++++ Mage.Sets/src/mage/sets/UrzasDestiny.java | 334 +++++++++--------- 3 files changed, 302 insertions(+), 166 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/m/MetathranElite.java create mode 100644 Mage.Sets/src/mage/cards/s/ScryingGlass.java diff --git a/Mage.Sets/src/mage/cards/m/MetathranElite.java b/Mage.Sets/src/mage/cards/m/MetathranElite.java new file mode 100644 index 00000000000..cfeed06ccfe --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MetathranElite.java @@ -0,0 +1,47 @@ +package mage.cards.m; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.EnchantedSourceCondition; +import mage.abilities.decorator.ConditionalRestrictionEffect; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; + +/** + * + * @author jeffwadsworth + */ +public final class MetathranElite extends CardImpl { + + private static final String rule = "{this} is unblockable as long as it's enchanted."; + + public MetathranElite(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); + + this.subtype.add(SubType.METATHRAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Metathran Elite is unblockable as long as it's enchanted. + ConditionalRestrictionEffect effect = new ConditionalRestrictionEffect( + new CantBeBlockedSourceEffect(), new EnchantedSourceCondition()); + effect.setText(rule); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + + } + + public MetathranElite(final MetathranElite card) { + super(card); + } + + @Override + public MetathranElite copy() { + return new MetathranElite(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScryingGlass.java b/Mage.Sets/src/mage/cards/s/ScryingGlass.java new file mode 100644 index 00000000000..8f53b6f0333 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScryingGlass.java @@ -0,0 +1,87 @@ +package mage.cards.s; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.ChoiceColor; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +/** + * + * @author jeffwadsworth + */ +public final class ScryingGlass extends CardImpl { + + public ScryingGlass(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + // {3}, {tap}: Choose a number greater than 0 and a color. Target opponent reveals his or her hand. If that opponent reveals exactly the chosen number of cards of the chosen color, you draw a card. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ScryingGlassEffect(), new ManaCostsImpl("{3}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + + } + + public ScryingGlass(final ScryingGlass card) { + super(card); + } + + @Override + public ScryingGlass copy() { + return new ScryingGlass(this); + } +} + +class ScryingGlassEffect extends OneShotEffect { + + public ScryingGlassEffect() { + super(Outcome.Neutral); + staticText = "Choose a number greater than 0 and a color. Target opponent reveals his or her hand. If that opponent reveals exactly the chosen number of cards of the chosen color, you draw a card"; + } + + public ScryingGlassEffect(final ScryingGlassEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player targetOpponent = game.getPlayer(source.getFirstTarget()); + ChoiceColor color = new ChoiceColor(); + int amount = 0; + if (controller != null + && targetOpponent != null) { + amount = controller.getAmount(1, Integer.MAX_VALUE, "Choose a number", game); + controller.choose(Outcome.Discard, color, game); + FilterCard filter = new FilterCard(); + filter.add(new ColorPredicate(color.getColor())); + targetOpponent.revealCards(source, targetOpponent.getHand(), game); + if (targetOpponent.getHand().count(filter, game) == amount) { + game.informPlayers(controller.getName() + " has chosen the exact number and color of the revealed cards from " + targetOpponent.getName() + "'s hand. They draw a card."); + controller.drawCards(1, game); + return true; + } else { + game.informPlayers(controller.getName() + " has chosen incorrectly and will not draw a card."); + } + } + return false; + } + + @Override + public ScryingGlassEffect copy() { + return new ScryingGlassEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/UrzasDestiny.java b/Mage.Sets/src/mage/sets/UrzasDestiny.java index 17e0fd38ed1..810b12ccff6 100644 --- a/Mage.Sets/src/mage/sets/UrzasDestiny.java +++ b/Mage.Sets/src/mage/sets/UrzasDestiny.java @@ -1,166 +1,168 @@ - -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * - * @author Backfir3 - */ -public final class UrzasDestiny extends ExpansionSet { - - private static final UrzasDestiny instance = new UrzasDestiny(); - - public static UrzasDestiny getInstance() { - return instance; - } - - private UrzasDestiny() { - super("Urza's Destiny", "UDS", ExpansionSet.buildDate(1999, 6, 7), SetType.EXPANSION); - this.blockName = "Urza"; - this.parentSet = UrzasSaga.getInstance(); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Academy Rector", 1, Rarity.RARE, mage.cards.a.AcademyRector.class)); - cards.add(new SetCardInfo("Aether Sting", 76, Rarity.UNCOMMON, mage.cards.a.AetherSting.class)); - cards.add(new SetCardInfo("Ancient Silverback", 101, Rarity.RARE, mage.cards.a.AncientSilverback.class)); - cards.add(new SetCardInfo("Apprentice Necromancer", 51, Rarity.RARE, mage.cards.a.ApprenticeNecromancer.class)); - cards.add(new SetCardInfo("Attrition", 52, Rarity.RARE, mage.cards.a.Attrition.class)); - cards.add(new SetCardInfo("Aura Thief", 26, Rarity.RARE, mage.cards.a.AuraThief.class)); - cards.add(new SetCardInfo("Blizzard Elemental", 27, Rarity.RARE, mage.cards.b.BlizzardElemental.class)); - cards.add(new SetCardInfo("Bloodshot Cyclops", 77, Rarity.RARE, mage.cards.b.BloodshotCyclops.class)); - cards.add(new SetCardInfo("Body Snatcher", 53, Rarity.RARE, mage.cards.b.BodySnatcher.class)); - cards.add(new SetCardInfo("Braidwood Cup", 126, Rarity.UNCOMMON, mage.cards.b.BraidwoodCup.class)); - cards.add(new SetCardInfo("Braidwood Sextant", 127, Rarity.UNCOMMON, mage.cards.b.BraidwoodSextant.class)); - cards.add(new SetCardInfo("Brass Secretary", 128, Rarity.UNCOMMON, mage.cards.b.BrassSecretary.class)); - cards.add(new SetCardInfo("Brine Seer", 28, Rarity.UNCOMMON, mage.cards.b.BrineSeer.class)); - cards.add(new SetCardInfo("Bubbling Beebles", 29, Rarity.COMMON, mage.cards.b.BubblingBeebles.class)); - cards.add(new SetCardInfo("Bubbling Muck", 54, Rarity.COMMON, mage.cards.b.BubblingMuck.class)); - cards.add(new SetCardInfo("Caltrops", 129, Rarity.UNCOMMON, mage.cards.c.Caltrops.class)); - cards.add(new SetCardInfo("Capashen Knight", 3, Rarity.COMMON, mage.cards.c.CapashenKnight.class)); - cards.add(new SetCardInfo("Capashen Standard", 4, Rarity.COMMON, mage.cards.c.CapashenStandard.class)); - cards.add(new SetCardInfo("Capashen Templar", 5, Rarity.COMMON, mage.cards.c.CapashenTemplar.class)); - cards.add(new SetCardInfo("Carnival of Souls", 55, Rarity.RARE, mage.cards.c.CarnivalOfSouls.class)); - cards.add(new SetCardInfo("Chime of Night", 56, Rarity.COMMON, mage.cards.c.ChimeOfNight.class)); - cards.add(new SetCardInfo("Cinder Seer", 78, Rarity.UNCOMMON, mage.cards.c.CinderSeer.class)); - cards.add(new SetCardInfo("Colos Yearling", 79, Rarity.COMMON, mage.cards.c.ColosYearling.class)); - cards.add(new SetCardInfo("Compost", 102, Rarity.UNCOMMON, mage.cards.c.Compost.class)); - cards.add(new SetCardInfo("Covetous Dragon", 80, Rarity.RARE, mage.cards.c.CovetousDragon.class)); - cards.add(new SetCardInfo("Disease Carriers", 57, Rarity.COMMON, mage.cards.d.DiseaseCarriers.class)); - cards.add(new SetCardInfo("Donate", 31, Rarity.RARE, mage.cards.d.Donate.class)); - cards.add(new SetCardInfo("Dying Wail", 58, Rarity.COMMON, mage.cards.d.DyingWail.class)); - cards.add(new SetCardInfo("Elvish Lookout", 103, Rarity.COMMON, mage.cards.e.ElvishLookout.class)); - cards.add(new SetCardInfo("Elvish Piper", 104, Rarity.RARE, mage.cards.e.ElvishPiper.class)); - cards.add(new SetCardInfo("Emperor Crocodile", 105, Rarity.RARE, mage.cards.e.EmperorCrocodile.class)); - cards.add(new SetCardInfo("Encroach", 59, Rarity.UNCOMMON, mage.cards.e.Encroach.class)); - cards.add(new SetCardInfo("Eradicate", 60, Rarity.UNCOMMON, mage.cards.e.Eradicate.class)); - cards.add(new SetCardInfo("Extruder", 130, Rarity.UNCOMMON, mage.cards.e.Extruder.class)); - cards.add(new SetCardInfo("False Prophet", 6, Rarity.RARE, mage.cards.f.FalseProphet.class)); - cards.add(new SetCardInfo("Festering Wound", 61, Rarity.UNCOMMON, mage.cards.f.FesteringWound.class)); - cards.add(new SetCardInfo("Field Surgeon", 8, Rarity.COMMON, mage.cards.f.FieldSurgeon.class)); - cards.add(new SetCardInfo("Flame Jet", 81, Rarity.COMMON, mage.cards.f.FlameJet.class)); - cards.add(new SetCardInfo("Fledgling Osprey", 33, Rarity.COMMON, mage.cards.f.FledglingOsprey.class)); - cards.add(new SetCardInfo("Flicker", 9, Rarity.RARE, mage.cards.f.Flicker.class)); - cards.add(new SetCardInfo("Fodder Cannon", 131, Rarity.UNCOMMON, mage.cards.f.FodderCannon.class)); - cards.add(new SetCardInfo("Gamekeeper", 106, Rarity.UNCOMMON, mage.cards.g.Gamekeeper.class)); - cards.add(new SetCardInfo("Goblin Berserker", 82, Rarity.UNCOMMON, mage.cards.g.GoblinBerserker.class)); - cards.add(new SetCardInfo("Goblin Festival", 83, Rarity.RARE, mage.cards.g.GoblinFestival.class)); - cards.add(new SetCardInfo("Goblin Gardener", 84, Rarity.COMMON, mage.cards.g.GoblinGardener.class)); - cards.add(new SetCardInfo("Goblin Marshal", 85, Rarity.RARE, mage.cards.g.GoblinMarshal.class)); - cards.add(new SetCardInfo("Goblin Masons", 86, Rarity.COMMON, mage.cards.g.GoblinMasons.class)); - cards.add(new SetCardInfo("Goliath Beetle", 107, Rarity.COMMON, mage.cards.g.GoliathBeetle.class)); - cards.add(new SetCardInfo("Heart Warden", 108, Rarity.COMMON, mage.cards.h.HeartWarden.class)); - cards.add(new SetCardInfo("Hulking Ogre", 87, Rarity.COMMON, mage.cards.h.HulkingOgre.class)); - cards.add(new SetCardInfo("Hunting Moa", 109, Rarity.UNCOMMON, mage.cards.h.HuntingMoa.class)); - cards.add(new SetCardInfo("Illuminated Wings", 34, Rarity.COMMON, mage.cards.i.IlluminatedWings.class)); - cards.add(new SetCardInfo("Impatience", 88, Rarity.RARE, mage.cards.i.Impatience.class)); - cards.add(new SetCardInfo("Iridescent Drake", 35, Rarity.UNCOMMON, mage.cards.i.IridescentDrake.class)); - cards.add(new SetCardInfo("Ivy Seer", 110, Rarity.UNCOMMON, mage.cards.i.IvySeer.class)); - cards.add(new SetCardInfo("Jasmine Seer", 10, Rarity.UNCOMMON, mage.cards.j.JasmineSeer.class)); - cards.add(new SetCardInfo("Junk Diver", 132, Rarity.RARE, mage.cards.j.JunkDiver.class)); - cards.add(new SetCardInfo("Keldon Champion", 90, Rarity.UNCOMMON, mage.cards.k.KeldonChampion.class)); - cards.add(new SetCardInfo("Keldon Vandals", 91, Rarity.COMMON, mage.cards.k.KeldonVandals.class)); - cards.add(new SetCardInfo("Kingfisher", 36, Rarity.COMMON, mage.cards.k.Kingfisher.class)); - cards.add(new SetCardInfo("Landslide", 92, Rarity.UNCOMMON, mage.cards.l.Landslide.class)); - cards.add(new SetCardInfo("Magnify", 111, Rarity.COMMON, mage.cards.m.Magnify.class)); - cards.add(new SetCardInfo("Mantis Engine", 133, Rarity.UNCOMMON, mage.cards.m.MantisEngine.class)); - cards.add(new SetCardInfo("Marker Beetles", 112, Rarity.COMMON, mage.cards.m.MarkerBeetles.class)); - cards.add(new SetCardInfo("Mark of Fury", 93, Rarity.COMMON, mage.cards.m.MarkOfFury.class)); - cards.add(new SetCardInfo("Mask of Law and Grace", 11, Rarity.COMMON, mage.cards.m.MaskOfLawAndGrace.class)); - cards.add(new SetCardInfo("Master Healer", 12, Rarity.RARE, mage.cards.m.MasterHealer.class)); - cards.add(new SetCardInfo("Masticore", 134, Rarity.RARE, mage.cards.m.Masticore.class)); - cards.add(new SetCardInfo("Mental Discipline", 37, Rarity.COMMON, mage.cards.m.MentalDiscipline.class)); - cards.add(new SetCardInfo("Metalworker", 135, Rarity.RARE, mage.cards.m.Metalworker.class)); - cards.add(new SetCardInfo("Metathran Soldier", 39, Rarity.COMMON, mage.cards.m.MetathranSoldier.class)); - cards.add(new SetCardInfo("Momentum", 113, Rarity.UNCOMMON, mage.cards.m.Momentum.class)); - cards.add(new SetCardInfo("Multani's Decree", 114, Rarity.COMMON, mage.cards.m.MultanisDecree.class)); - cards.add(new SetCardInfo("Nightshade Seer", 63, Rarity.UNCOMMON, mage.cards.n.NightshadeSeer.class)); - cards.add(new SetCardInfo("Opalescence", 13, Rarity.RARE, mage.cards.o.Opalescence.class)); - cards.add(new SetCardInfo("Opposition", 40, Rarity.RARE, mage.cards.o.Opposition.class)); - cards.add(new SetCardInfo("Pattern of Rebirth", 115, Rarity.RARE, mage.cards.p.PatternOfRebirth.class)); - cards.add(new SetCardInfo("Phyrexian Monitor", 64, Rarity.COMMON, mage.cards.p.PhyrexianMonitor.class)); - cards.add(new SetCardInfo("Phyrexian Negator", 65, Rarity.RARE, mage.cards.p.PhyrexianNegator.class)); - cards.add(new SetCardInfo("Plague Dogs", 66, Rarity.UNCOMMON, mage.cards.p.PlagueDogs.class)); - cards.add(new SetCardInfo("Plated Spider", 116, Rarity.COMMON, mage.cards.p.PlatedSpider.class)); - cards.add(new SetCardInfo("Plow Under", 117, Rarity.RARE, mage.cards.p.PlowUnder.class)); - cards.add(new SetCardInfo("Powder Keg", 136, Rarity.RARE, mage.cards.p.PowderKeg.class)); - cards.add(new SetCardInfo("Quash", 42, Rarity.UNCOMMON, mage.cards.q.Quash.class)); - cards.add(new SetCardInfo("Rapid Decay", 67, Rarity.RARE, mage.cards.r.RapidDecay.class)); - cards.add(new SetCardInfo("Ravenous Rats", 68, Rarity.COMMON, mage.cards.r.RavenousRats.class)); - cards.add(new SetCardInfo("Rayne, Academy Chancellor", 43, Rarity.RARE, mage.cards.r.RayneAcademyChancellor.class)); - cards.add(new SetCardInfo("Reckless Abandon", 94, Rarity.COMMON, mage.cards.r.RecklessAbandon.class)); - cards.add(new SetCardInfo("Reliquary Monk", 14, Rarity.COMMON, mage.cards.r.ReliquaryMonk.class)); - cards.add(new SetCardInfo("Repercussion", 95, Rarity.RARE, mage.cards.r.Repercussion.class)); - cards.add(new SetCardInfo("Replenish", 15, Rarity.RARE, mage.cards.r.Replenish.class)); - cards.add(new SetCardInfo("Rescue", 44, Rarity.COMMON, mage.cards.r.Rescue.class)); - cards.add(new SetCardInfo("Rofellos's Gift", 119, Rarity.COMMON, mage.cards.r.RofellossGift.class)); - cards.add(new SetCardInfo("Rofellos, Llanowar Emissary", 118, Rarity.RARE, mage.cards.r.RofellosLlanowarEmissary.class)); - cards.add(new SetCardInfo("Sanctimony", 16, Rarity.UNCOMMON, mage.cards.s.Sanctimony.class)); - cards.add(new SetCardInfo("Scent of Brine", 45, Rarity.COMMON, mage.cards.s.ScentOfBrine.class)); - cards.add(new SetCardInfo("Scent of Cinder", 96, Rarity.COMMON, mage.cards.s.ScentOfCinder.class)); - cards.add(new SetCardInfo("Scent of Ivy", 120, Rarity.COMMON, mage.cards.s.ScentOfIvy.class)); - cards.add(new SetCardInfo("Scent of Jasmine", 17, Rarity.COMMON, mage.cards.s.ScentOfJasmine.class)); - cards.add(new SetCardInfo("Scent of Nightshade", 69, Rarity.COMMON, mage.cards.s.ScentOfNightshade.class)); - cards.add(new SetCardInfo("Scour", 18, Rarity.UNCOMMON, mage.cards.s.Scour.class)); - cards.add(new SetCardInfo("Serra Advocate", 19, Rarity.UNCOMMON, mage.cards.s.SerraAdvocate.class)); - cards.add(new SetCardInfo("Sigil of Sleep", 46, Rarity.COMMON, mage.cards.s.SigilOfSleep.class)); - cards.add(new SetCardInfo("Skittering Horror", 70, Rarity.COMMON, mage.cards.s.SkitteringHorror.class)); - cards.add(new SetCardInfo("Slinking Skirge", 71, Rarity.COMMON, mage.cards.s.SlinkingSkirge.class)); - cards.add(new SetCardInfo("Solidarity", 20, Rarity.COMMON, mage.cards.s.Solidarity.class)); - cards.add(new SetCardInfo("Soul Feast", 72, Rarity.UNCOMMON, mage.cards.s.SoulFeast.class)); - cards.add(new SetCardInfo("Sowing Salt", 97, Rarity.UNCOMMON, mage.cards.s.SowingSalt.class)); - cards.add(new SetCardInfo("Splinter", 121, Rarity.UNCOMMON, mage.cards.s.Splinter.class)); - cards.add(new SetCardInfo("Squirming Mass", 73, Rarity.COMMON, mage.cards.s.SquirmingMass.class)); - cards.add(new SetCardInfo("Storage Matrix", 138, Rarity.RARE, mage.cards.s.StorageMatrix.class)); - cards.add(new SetCardInfo("Taunting Elf", 122, Rarity.COMMON, mage.cards.t.TauntingElf.class)); - cards.add(new SetCardInfo("Telepathic Spies", 47, Rarity.COMMON, mage.cards.t.TelepathicSpies.class)); - cards.add(new SetCardInfo("Temporal Adept", 48, Rarity.RARE, mage.cards.t.TemporalAdept.class)); - cards.add(new SetCardInfo("Tethered Griffin", 21, Rarity.RARE, mage.cards.t.TetheredGriffin.class)); - cards.add(new SetCardInfo("Thieving Magpie", 49, Rarity.UNCOMMON, mage.cards.t.ThievingMagpie.class)); - cards.add(new SetCardInfo("Thorn Elemental", 123, Rarity.RARE, mage.cards.t.ThornElemental.class)); - cards.add(new SetCardInfo("Thran Dynamo", 139, Rarity.UNCOMMON, mage.cards.t.ThranDynamo.class)); - cards.add(new SetCardInfo("Thran Foundry", 140, Rarity.UNCOMMON, mage.cards.t.ThranFoundry.class)); - cards.add(new SetCardInfo("Thran Golem", 141, Rarity.RARE, mage.cards.t.ThranGolem.class)); - cards.add(new SetCardInfo("Tormented Angel", 22, Rarity.COMMON, mage.cards.t.TormentedAngel.class)); - cards.add(new SetCardInfo("Treachery", 50, Rarity.RARE, mage.cards.t.Treachery.class)); - cards.add(new SetCardInfo("Trumpet Blast", 98, Rarity.COMMON, mage.cards.t.TrumpetBlast.class)); - cards.add(new SetCardInfo("Twisted Experiment", 74, Rarity.COMMON, mage.cards.t.TwistedExperiment.class)); - cards.add(new SetCardInfo("Urza's Incubator", 142, Rarity.RARE, mage.cards.u.UrzasIncubator.class)); - cards.add(new SetCardInfo("Voice of Duty", 23, Rarity.UNCOMMON, mage.cards.v.VoiceOfDuty.class)); - cards.add(new SetCardInfo("Voice of Reason", 24, Rarity.UNCOMMON, mage.cards.v.VoiceOfReason.class)); - cards.add(new SetCardInfo("Wake of Destruction", 99, Rarity.RARE, mage.cards.w.WakeOfDestruction.class)); - cards.add(new SetCardInfo("Wall of Glare", 25, Rarity.COMMON, mage.cards.w.WallOfGlare.class)); - cards.add(new SetCardInfo("Wild Colos", 100, Rarity.COMMON, mage.cards.w.WildColos.class)); - cards.add(new SetCardInfo("Yavimaya Elder", 124, Rarity.COMMON, mage.cards.y.YavimayaElder.class)); - cards.add(new SetCardInfo("Yavimaya Enchantress", 125, Rarity.UNCOMMON, mage.cards.y.YavimayaEnchantress.class)); - cards.add(new SetCardInfo("Yavimaya Hollow", 143, Rarity.RARE, mage.cards.y.YavimayaHollow.class)); - cards.add(new SetCardInfo("Yawgmoth's Bargain", 75, Rarity.RARE, mage.cards.y.YawgmothsBargain.class)); - } -} + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * + * @author Backfir3 + */ +public final class UrzasDestiny extends ExpansionSet { + + private static final UrzasDestiny instance = new UrzasDestiny(); + + public static UrzasDestiny getInstance() { + return instance; + } + + private UrzasDestiny() { + super("Urza's Destiny", "UDS", ExpansionSet.buildDate(1999, 6, 7), SetType.EXPANSION); + this.blockName = "Urza"; + this.parentSet = UrzasSaga.getInstance(); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Academy Rector", 1, Rarity.RARE, mage.cards.a.AcademyRector.class)); + cards.add(new SetCardInfo("Aether Sting", 76, Rarity.UNCOMMON, mage.cards.a.AetherSting.class)); + cards.add(new SetCardInfo("Ancient Silverback", 101, Rarity.RARE, mage.cards.a.AncientSilverback.class)); + cards.add(new SetCardInfo("Apprentice Necromancer", 51, Rarity.RARE, mage.cards.a.ApprenticeNecromancer.class)); + cards.add(new SetCardInfo("Attrition", 52, Rarity.RARE, mage.cards.a.Attrition.class)); + cards.add(new SetCardInfo("Aura Thief", 26, Rarity.RARE, mage.cards.a.AuraThief.class)); + cards.add(new SetCardInfo("Blizzard Elemental", 27, Rarity.RARE, mage.cards.b.BlizzardElemental.class)); + cards.add(new SetCardInfo("Bloodshot Cyclops", 77, Rarity.RARE, mage.cards.b.BloodshotCyclops.class)); + cards.add(new SetCardInfo("Body Snatcher", 53, Rarity.RARE, mage.cards.b.BodySnatcher.class)); + cards.add(new SetCardInfo("Braidwood Cup", 126, Rarity.UNCOMMON, mage.cards.b.BraidwoodCup.class)); + cards.add(new SetCardInfo("Braidwood Sextant", 127, Rarity.UNCOMMON, mage.cards.b.BraidwoodSextant.class)); + cards.add(new SetCardInfo("Brass Secretary", 128, Rarity.UNCOMMON, mage.cards.b.BrassSecretary.class)); + cards.add(new SetCardInfo("Brine Seer", 28, Rarity.UNCOMMON, mage.cards.b.BrineSeer.class)); + cards.add(new SetCardInfo("Bubbling Beebles", 29, Rarity.COMMON, mage.cards.b.BubblingBeebles.class)); + cards.add(new SetCardInfo("Bubbling Muck", 54, Rarity.COMMON, mage.cards.b.BubblingMuck.class)); + cards.add(new SetCardInfo("Caltrops", 129, Rarity.UNCOMMON, mage.cards.c.Caltrops.class)); + cards.add(new SetCardInfo("Capashen Knight", 3, Rarity.COMMON, mage.cards.c.CapashenKnight.class)); + cards.add(new SetCardInfo("Capashen Standard", 4, Rarity.COMMON, mage.cards.c.CapashenStandard.class)); + cards.add(new SetCardInfo("Capashen Templar", 5, Rarity.COMMON, mage.cards.c.CapashenTemplar.class)); + cards.add(new SetCardInfo("Carnival of Souls", 55, Rarity.RARE, mage.cards.c.CarnivalOfSouls.class)); + cards.add(new SetCardInfo("Chime of Night", 56, Rarity.COMMON, mage.cards.c.ChimeOfNight.class)); + cards.add(new SetCardInfo("Cinder Seer", 78, Rarity.UNCOMMON, mage.cards.c.CinderSeer.class)); + cards.add(new SetCardInfo("Colos Yearling", 79, Rarity.COMMON, mage.cards.c.ColosYearling.class)); + cards.add(new SetCardInfo("Compost", 102, Rarity.UNCOMMON, mage.cards.c.Compost.class)); + cards.add(new SetCardInfo("Covetous Dragon", 80, Rarity.RARE, mage.cards.c.CovetousDragon.class)); + cards.add(new SetCardInfo("Disease Carriers", 57, Rarity.COMMON, mage.cards.d.DiseaseCarriers.class)); + cards.add(new SetCardInfo("Donate", 31, Rarity.RARE, mage.cards.d.Donate.class)); + cards.add(new SetCardInfo("Dying Wail", 58, Rarity.COMMON, mage.cards.d.DyingWail.class)); + cards.add(new SetCardInfo("Elvish Lookout", 103, Rarity.COMMON, mage.cards.e.ElvishLookout.class)); + cards.add(new SetCardInfo("Elvish Piper", 104, Rarity.RARE, mage.cards.e.ElvishPiper.class)); + cards.add(new SetCardInfo("Emperor Crocodile", 105, Rarity.RARE, mage.cards.e.EmperorCrocodile.class)); + cards.add(new SetCardInfo("Encroach", 59, Rarity.UNCOMMON, mage.cards.e.Encroach.class)); + cards.add(new SetCardInfo("Eradicate", 60, Rarity.UNCOMMON, mage.cards.e.Eradicate.class)); + cards.add(new SetCardInfo("Extruder", 130, Rarity.UNCOMMON, mage.cards.e.Extruder.class)); + cards.add(new SetCardInfo("False Prophet", 6, Rarity.RARE, mage.cards.f.FalseProphet.class)); + cards.add(new SetCardInfo("Festering Wound", 61, Rarity.UNCOMMON, mage.cards.f.FesteringWound.class)); + cards.add(new SetCardInfo("Field Surgeon", 8, Rarity.COMMON, mage.cards.f.FieldSurgeon.class)); + cards.add(new SetCardInfo("Flame Jet", 81, Rarity.COMMON, mage.cards.f.FlameJet.class)); + cards.add(new SetCardInfo("Fledgling Osprey", 33, Rarity.COMMON, mage.cards.f.FledglingOsprey.class)); + cards.add(new SetCardInfo("Flicker", 9, Rarity.RARE, mage.cards.f.Flicker.class)); + cards.add(new SetCardInfo("Fodder Cannon", 131, Rarity.UNCOMMON, mage.cards.f.FodderCannon.class)); + cards.add(new SetCardInfo("Gamekeeper", 106, Rarity.UNCOMMON, mage.cards.g.Gamekeeper.class)); + cards.add(new SetCardInfo("Goblin Berserker", 82, Rarity.UNCOMMON, mage.cards.g.GoblinBerserker.class)); + cards.add(new SetCardInfo("Goblin Festival", 83, Rarity.RARE, mage.cards.g.GoblinFestival.class)); + cards.add(new SetCardInfo("Goblin Gardener", 84, Rarity.COMMON, mage.cards.g.GoblinGardener.class)); + cards.add(new SetCardInfo("Goblin Marshal", 85, Rarity.RARE, mage.cards.g.GoblinMarshal.class)); + cards.add(new SetCardInfo("Goblin Masons", 86, Rarity.COMMON, mage.cards.g.GoblinMasons.class)); + cards.add(new SetCardInfo("Goliath Beetle", 107, Rarity.COMMON, mage.cards.g.GoliathBeetle.class)); + cards.add(new SetCardInfo("Heart Warden", 108, Rarity.COMMON, mage.cards.h.HeartWarden.class)); + cards.add(new SetCardInfo("Hulking Ogre", 87, Rarity.COMMON, mage.cards.h.HulkingOgre.class)); + cards.add(new SetCardInfo("Hunting Moa", 109, Rarity.UNCOMMON, mage.cards.h.HuntingMoa.class)); + cards.add(new SetCardInfo("Illuminated Wings", 34, Rarity.COMMON, mage.cards.i.IlluminatedWings.class)); + cards.add(new SetCardInfo("Impatience", 88, Rarity.RARE, mage.cards.i.Impatience.class)); + cards.add(new SetCardInfo("Iridescent Drake", 35, Rarity.UNCOMMON, mage.cards.i.IridescentDrake.class)); + cards.add(new SetCardInfo("Ivy Seer", 110, Rarity.UNCOMMON, mage.cards.i.IvySeer.class)); + cards.add(new SetCardInfo("Jasmine Seer", 10, Rarity.UNCOMMON, mage.cards.j.JasmineSeer.class)); + cards.add(new SetCardInfo("Junk Diver", 132, Rarity.RARE, mage.cards.j.JunkDiver.class)); + cards.add(new SetCardInfo("Keldon Champion", 90, Rarity.UNCOMMON, mage.cards.k.KeldonChampion.class)); + cards.add(new SetCardInfo("Keldon Vandals", 91, Rarity.COMMON, mage.cards.k.KeldonVandals.class)); + cards.add(new SetCardInfo("Kingfisher", 36, Rarity.COMMON, mage.cards.k.Kingfisher.class)); + cards.add(new SetCardInfo("Landslide", 92, Rarity.UNCOMMON, mage.cards.l.Landslide.class)); + cards.add(new SetCardInfo("Magnify", 111, Rarity.COMMON, mage.cards.m.Magnify.class)); + cards.add(new SetCardInfo("Mantis Engine", 133, Rarity.UNCOMMON, mage.cards.m.MantisEngine.class)); + cards.add(new SetCardInfo("Marker Beetles", 112, Rarity.COMMON, mage.cards.m.MarkerBeetles.class)); + cards.add(new SetCardInfo("Mark of Fury", 93, Rarity.COMMON, mage.cards.m.MarkOfFury.class)); + cards.add(new SetCardInfo("Mask of Law and Grace", 11, Rarity.COMMON, mage.cards.m.MaskOfLawAndGrace.class)); + cards.add(new SetCardInfo("Master Healer", 12, Rarity.RARE, mage.cards.m.MasterHealer.class)); + cards.add(new SetCardInfo("Masticore", 134, Rarity.RARE, mage.cards.m.Masticore.class)); + cards.add(new SetCardInfo("Mental Discipline", 37, Rarity.COMMON, mage.cards.m.MentalDiscipline.class)); + cards.add(new SetCardInfo("Metalworker", 135, Rarity.RARE, mage.cards.m.Metalworker.class)); + cards.add(new SetCardInfo("Metathran Elite", 38, Rarity.UNCOMMON, mage.cards.m.MetathranElite.class)); + cards.add(new SetCardInfo("Metathran Soldier", 39, Rarity.COMMON, mage.cards.m.MetathranSoldier.class)); + cards.add(new SetCardInfo("Momentum", 113, Rarity.UNCOMMON, mage.cards.m.Momentum.class)); + cards.add(new SetCardInfo("Multani's Decree", 114, Rarity.COMMON, mage.cards.m.MultanisDecree.class)); + cards.add(new SetCardInfo("Nightshade Seer", 63, Rarity.UNCOMMON, mage.cards.n.NightshadeSeer.class)); + cards.add(new SetCardInfo("Opalescence", 13, Rarity.RARE, mage.cards.o.Opalescence.class)); + cards.add(new SetCardInfo("Opposition", 40, Rarity.RARE, mage.cards.o.Opposition.class)); + cards.add(new SetCardInfo("Pattern of Rebirth", 115, Rarity.RARE, mage.cards.p.PatternOfRebirth.class)); + cards.add(new SetCardInfo("Phyrexian Monitor", 64, Rarity.COMMON, mage.cards.p.PhyrexianMonitor.class)); + cards.add(new SetCardInfo("Phyrexian Negator", 65, Rarity.RARE, mage.cards.p.PhyrexianNegator.class)); + cards.add(new SetCardInfo("Plague Dogs", 66, Rarity.UNCOMMON, mage.cards.p.PlagueDogs.class)); + cards.add(new SetCardInfo("Plated Spider", 116, Rarity.COMMON, mage.cards.p.PlatedSpider.class)); + cards.add(new SetCardInfo("Plow Under", 117, Rarity.RARE, mage.cards.p.PlowUnder.class)); + cards.add(new SetCardInfo("Powder Keg", 136, Rarity.RARE, mage.cards.p.PowderKeg.class)); + cards.add(new SetCardInfo("Quash", 42, Rarity.UNCOMMON, mage.cards.q.Quash.class)); + cards.add(new SetCardInfo("Rapid Decay", 67, Rarity.RARE, mage.cards.r.RapidDecay.class)); + cards.add(new SetCardInfo("Ravenous Rats", 68, Rarity.COMMON, mage.cards.r.RavenousRats.class)); + cards.add(new SetCardInfo("Rayne, Academy Chancellor", 43, Rarity.RARE, mage.cards.r.RayneAcademyChancellor.class)); + cards.add(new SetCardInfo("Reckless Abandon", 94, Rarity.COMMON, mage.cards.r.RecklessAbandon.class)); + cards.add(new SetCardInfo("Reliquary Monk", 14, Rarity.COMMON, mage.cards.r.ReliquaryMonk.class)); + cards.add(new SetCardInfo("Repercussion", 95, Rarity.RARE, mage.cards.r.Repercussion.class)); + cards.add(new SetCardInfo("Replenish", 15, Rarity.RARE, mage.cards.r.Replenish.class)); + cards.add(new SetCardInfo("Rescue", 44, Rarity.COMMON, mage.cards.r.Rescue.class)); + cards.add(new SetCardInfo("Rofellos's Gift", 119, Rarity.COMMON, mage.cards.r.RofellossGift.class)); + cards.add(new SetCardInfo("Rofellos, Llanowar Emissary", 118, Rarity.RARE, mage.cards.r.RofellosLlanowarEmissary.class)); + cards.add(new SetCardInfo("Sanctimony", 16, Rarity.UNCOMMON, mage.cards.s.Sanctimony.class)); + cards.add(new SetCardInfo("Scent of Brine", 45, Rarity.COMMON, mage.cards.s.ScentOfBrine.class)); + cards.add(new SetCardInfo("Scent of Cinder", 96, Rarity.COMMON, mage.cards.s.ScentOfCinder.class)); + cards.add(new SetCardInfo("Scent of Ivy", 120, Rarity.COMMON, mage.cards.s.ScentOfIvy.class)); + cards.add(new SetCardInfo("Scent of Jasmine", 17, Rarity.COMMON, mage.cards.s.ScentOfJasmine.class)); + cards.add(new SetCardInfo("Scent of Nightshade", 69, Rarity.COMMON, mage.cards.s.ScentOfNightshade.class)); + cards.add(new SetCardInfo("Scour", 18, Rarity.UNCOMMON, mage.cards.s.Scour.class)); + cards.add(new SetCardInfo("Scrying Glass", 137, Rarity.RARE, mage.cards.s.ScryingGlass.class)); + cards.add(new SetCardInfo("Serra Advocate", 19, Rarity.UNCOMMON, mage.cards.s.SerraAdvocate.class)); + cards.add(new SetCardInfo("Sigil of Sleep", 46, Rarity.COMMON, mage.cards.s.SigilOfSleep.class)); + cards.add(new SetCardInfo("Skittering Horror", 70, Rarity.COMMON, mage.cards.s.SkitteringHorror.class)); + cards.add(new SetCardInfo("Slinking Skirge", 71, Rarity.COMMON, mage.cards.s.SlinkingSkirge.class)); + cards.add(new SetCardInfo("Solidarity", 20, Rarity.COMMON, mage.cards.s.Solidarity.class)); + cards.add(new SetCardInfo("Soul Feast", 72, Rarity.UNCOMMON, mage.cards.s.SoulFeast.class)); + cards.add(new SetCardInfo("Sowing Salt", 97, Rarity.UNCOMMON, mage.cards.s.SowingSalt.class)); + cards.add(new SetCardInfo("Splinter", 121, Rarity.UNCOMMON, mage.cards.s.Splinter.class)); + cards.add(new SetCardInfo("Squirming Mass", 73, Rarity.COMMON, mage.cards.s.SquirmingMass.class)); + cards.add(new SetCardInfo("Storage Matrix", 138, Rarity.RARE, mage.cards.s.StorageMatrix.class)); + cards.add(new SetCardInfo("Taunting Elf", 122, Rarity.COMMON, mage.cards.t.TauntingElf.class)); + cards.add(new SetCardInfo("Telepathic Spies", 47, Rarity.COMMON, mage.cards.t.TelepathicSpies.class)); + cards.add(new SetCardInfo("Temporal Adept", 48, Rarity.RARE, mage.cards.t.TemporalAdept.class)); + cards.add(new SetCardInfo("Tethered Griffin", 21, Rarity.RARE, mage.cards.t.TetheredGriffin.class)); + cards.add(new SetCardInfo("Thieving Magpie", 49, Rarity.UNCOMMON, mage.cards.t.ThievingMagpie.class)); + cards.add(new SetCardInfo("Thorn Elemental", 123, Rarity.RARE, mage.cards.t.ThornElemental.class)); + cards.add(new SetCardInfo("Thran Dynamo", 139, Rarity.UNCOMMON, mage.cards.t.ThranDynamo.class)); + cards.add(new SetCardInfo("Thran Foundry", 140, Rarity.UNCOMMON, mage.cards.t.ThranFoundry.class)); + cards.add(new SetCardInfo("Thran Golem", 141, Rarity.RARE, mage.cards.t.ThranGolem.class)); + cards.add(new SetCardInfo("Tormented Angel", 22, Rarity.COMMON, mage.cards.t.TormentedAngel.class)); + cards.add(new SetCardInfo("Treachery", 50, Rarity.RARE, mage.cards.t.Treachery.class)); + cards.add(new SetCardInfo("Trumpet Blast", 98, Rarity.COMMON, mage.cards.t.TrumpetBlast.class)); + cards.add(new SetCardInfo("Twisted Experiment", 74, Rarity.COMMON, mage.cards.t.TwistedExperiment.class)); + cards.add(new SetCardInfo("Urza's Incubator", 142, Rarity.RARE, mage.cards.u.UrzasIncubator.class)); + cards.add(new SetCardInfo("Voice of Duty", 23, Rarity.UNCOMMON, mage.cards.v.VoiceOfDuty.class)); + cards.add(new SetCardInfo("Voice of Reason", 24, Rarity.UNCOMMON, mage.cards.v.VoiceOfReason.class)); + cards.add(new SetCardInfo("Wake of Destruction", 99, Rarity.RARE, mage.cards.w.WakeOfDestruction.class)); + cards.add(new SetCardInfo("Wall of Glare", 25, Rarity.COMMON, mage.cards.w.WallOfGlare.class)); + cards.add(new SetCardInfo("Wild Colos", 100, Rarity.COMMON, mage.cards.w.WildColos.class)); + cards.add(new SetCardInfo("Yavimaya Elder", 124, Rarity.COMMON, mage.cards.y.YavimayaElder.class)); + cards.add(new SetCardInfo("Yavimaya Enchantress", 125, Rarity.UNCOMMON, mage.cards.y.YavimayaEnchantress.class)); + cards.add(new SetCardInfo("Yavimaya Hollow", 143, Rarity.RARE, mage.cards.y.YavimayaHollow.class)); + cards.add(new SetCardInfo("Yawgmoth's Bargain", 75, Rarity.RARE, mage.cards.y.YawgmothsBargain.class)); + } +} From 92d1b5c51af676a089d44b1d11d9cd1e6d4449d8 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 17 Nov 2018 02:48:50 +0400 Subject: [PATCH 16/61] * UI: added new skill level column with star icons instead text; --- .../java/mage/client/table/TablesPanel.java | 3493 +++++++++-------- .../mage/card/arcane/CardRendererUtils.java | 35 +- .../card/arcane/ManaSymbolsCellRenderer.java | 39 +- .../main/resources/info/yellow_star_16.png | Bin 0 -> 478 bytes .../main/resources/info/yellow_star_24.png | Bin 0 -> 658 bytes .../main/resources/info/yellow_star_32.png | Bin 0 -> 931 bytes 6 files changed, 1817 insertions(+), 1750 deletions(-) create mode 100644 Mage.Client/src/main/resources/info/yellow_star_16.png create mode 100644 Mage.Client/src/main/resources/info/yellow_star_24.png create mode 100644 Mage.Client/src/main/resources/info/yellow_star_32.png 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 59c4d888053..30c5d8e4ac9 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -1,163 +1,193 @@ - - /* - * TablesPanel.java - * - * Created on 15-Dec-2009, 10:54:01 PM - */ -package mage.client.table; + * TablesPanel.java + * + * Created on 15-Dec-2009, 10:54:01 PM + */ + package mage.client.table; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.beans.PropertyVetoException; -import java.io.File; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import javax.swing.*; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableCellRenderer; -import mage.cards.decks.importer.DeckImporterUtil; -import mage.client.MageFrame; -import mage.client.SessionHandler; -import mage.client.chat.ChatPanelBasic; -import mage.client.components.MageComponents; -import mage.client.dialog.*; -import static mage.client.dialog.PreferencesDialog.KEY_TABLES_COLUMNS_ORDER; -import static mage.client.dialog.PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH; -import static mage.client.dialog.PreferencesDialog.KEY_TABLES_FILTER_SETTINGS; -import static mage.client.dialog.PreferencesDialog.KEY_TABLES_DIVIDER_LOCATION_1; -import static mage.client.dialog.PreferencesDialog.KEY_TABLES_DIVIDER_LOCATION_2; -import static mage.client.dialog.PreferencesDialog.KEY_TABLES_DIVIDER_LOCATION_3; -import mage.client.util.ButtonColumn; -import mage.client.util.GUISizeHelper; -import mage.client.util.IgnoreList; -import mage.client.util.MageTableRowSorter; -import mage.client.util.URLHandler; -import mage.client.util.gui.GuiDisplayUtil; -import mage.client.util.gui.TableUtil; -import mage.constants.*; -import mage.game.match.MatchOptions; -import mage.players.PlayerType; -import mage.remote.MageRemoteException; -import mage.view.MatchView; -import mage.view.RoomUsersView; -import mage.view.TableView; -import mage.view.UserRequestMessage; -import org.apache.log4j.Logger; -import org.ocpsoft.prettytime.Duration; -import org.ocpsoft.prettytime.PrettyTime; -import org.ocpsoft.prettytime.units.JustNow; + import mage.cards.decks.importer.DeckImporterUtil; + import mage.client.MageFrame; + import mage.client.SessionHandler; + import mage.client.chat.ChatPanelBasic; + import mage.client.components.MageComponents; + import mage.client.dialog.*; + import mage.client.util.*; + import mage.client.util.gui.GuiDisplayUtil; + import mage.client.util.gui.TableUtil; + import mage.constants.*; + import mage.game.match.MatchOptions; + import mage.players.PlayerType; + import mage.remote.MageRemoteException; + import mage.view.MatchView; + import mage.view.RoomUsersView; + import mage.view.TableView; + import mage.view.UserRequestMessage; + import org.apache.log4j.Logger; + import org.mage.card.arcane.CardRendererUtils; + import org.ocpsoft.prettytime.Duration; + import org.ocpsoft.prettytime.PrettyTime; + import org.ocpsoft.prettytime.units.JustNow; -/** - * - * @author BetaSteward_at_googlemail.com - */ -public class TablesPanel extends javax.swing.JPanel { + import javax.swing.*; + import javax.swing.border.EmptyBorder; + import javax.swing.table.AbstractTableModel; + import javax.swing.table.DefaultTableCellRenderer; + import javax.swing.table.TableCellRenderer; + import java.awt.*; + import java.awt.event.ActionEvent; + import java.awt.event.MouseAdapter; + import java.awt.event.MouseEvent; + import java.beans.PropertyVetoException; + import java.io.File; + import java.text.DateFormat; + import java.text.SimpleDateFormat; + import java.util.*; + import java.util.concurrent.CancellationException; + import java.util.concurrent.ExecutionException; + import java.util.concurrent.Executors; + import java.util.concurrent.TimeUnit; - private static final Logger LOGGER = Logger.getLogger(TablesPanel.class); - private static final int[] DEFAULT_COLUMNS_WIDTH = {35, 150, 120, 180, 80, 120, 80, 60, 40, 40, 60}; + import static mage.client.dialog.PreferencesDialog.*; - private final TableTableModel tableModel; - private final MatchesTableModel matchesModel; - private UUID roomId; - private UpdateTablesTask updateTablesTask; - private UpdatePlayersTask updatePlayersTask; - private UpdateMatchesTask updateMatchesTask; - private JoinTableDialog joinTableDialog; - private NewTableDialog newTableDialog; - private NewTournamentDialog newTournamentDialog; - private final GameChooser gameChooser; - private java.util.List messages; - private int currentMessage; - private final MageTableRowSorter activeTablesSorter; - private final MageTableRowSorter completedTablesSorter; + /** + * @author BetaSteward_at_googlemail.com + */ + public class TablesPanel extends javax.swing.JPanel { - private final ButtonColumn actionButton1; - private final ButtonColumn actionButton2; + private static final Logger LOGGER = Logger.getLogger(TablesPanel.class); + private static final int[] DEFAULT_COLUMNS_WIDTH = {35, 150, 120, 180, 80, 120, 80, 60, 40, 40, 60}; - final JToggleButton[] filterButtons; + private final TableTableModel tableModel; + private final MatchesTableModel matchesModel; + private UUID roomId; + private UpdateTablesTask updateTablesTask; + private UpdatePlayersTask updatePlayersTask; + private UpdateMatchesTask updateMatchesTask; + private JoinTableDialog joinTableDialog; + private NewTableDialog newTableDialog; + private NewTournamentDialog newTournamentDialog; + private final GameChooser gameChooser; + private java.util.List messages; + private int currentMessage; + private final MageTableRowSorter activeTablesSorter; + private final MageTableRowSorter completedTablesSorter; - // time formater - private PrettyTime timeFormater = new PrettyTime(); + private final ButtonColumn actionButton1; + private final ButtonColumn actionButton2; - // time ago renderer - TableCellRenderer timeAgoCellRenderer = new DefaultTableCellRenderer() { - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - Date d = (Date) value; - label.setText(timeFormater.format(d)); - return label; - } - }; + final JToggleButton[] filterButtons; - // duration renderer - TableCellRenderer durationCellRenderer = new DefaultTableCellRenderer() { - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - Long ms = (Long) value; + // time formater + private PrettyTime timeFormater = new PrettyTime(); - if (ms != 0) { - Duration dur = timeFormater.approximateDuration(new Date(ms)); - label.setText((timeFormater.formatDuration(dur))); - } else { - label.setText(""); - } - return label; - } - }; + // time ago renderer + TableCellRenderer timeAgoCellRenderer = new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + Date d = (Date) value; + label.setText(timeFormater.format(d)); + return label; + } + }; - // datetime render - TableCellRenderer datetimeCellRenderer = new DefaultTableCellRenderer() { - DateFormat datetimeFormater = new SimpleDateFormat("HH:mm:ss"); + // duration renderer + TableCellRenderer durationCellRenderer = new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + Long ms = (Long) value; - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - Date d = (Date) value; - if (d != null) { - label.setText(datetimeFormater.format(d)); - } else { - label.setText(""); - } + if (ms != 0) { + Duration dur = timeFormater.approximateDuration(new Date(ms)); + label.setText((timeFormater.formatDuration(dur))); + } else { + label.setText(""); + } + return label; + } + }; - return label; - } - }; + // datetime render + TableCellRenderer datetimeCellRenderer = new DefaultTableCellRenderer() { + DateFormat datetimeFormater = new SimpleDateFormat("HH:mm:ss"); - /** - * Creates new form TablesPanel - */ - public TablesPanel() { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + Date d = (Date) value; + if (d != null) { + label.setText(datetimeFormater.format(d)); + } else { + label.setText(""); + } - tableModel = new TableTableModel(); - matchesModel = new MatchesTableModel(); - gameChooser = new GameChooser(); + return label; + } + }; - initComponents(); - // tableModel.setSession(session); + // skill renderer + TableCellRenderer skillCellRenderer = new DefaultTableCellRenderer() { - // formater - timeFormater.setLocale(Locale.ENGLISH); - JustNow jn = timeFormater.getUnit(JustNow.class); - jn.setMaxQuantity(1000L * 30L); // 30 seconds gap (show "just now" from 0 to 30 secs) + // base panel to render + private JPanel renderPanel = new JPanel(); + private ImageIcon skillIcon = new ImageIcon(this.getClass().getResource("/info/yellow_star_16.png")); - // 1. TABLE CURRENT - tableTables.createDefaultColumnsFromModel(); - activeTablesSorter = new MageTableRowSorter(tableModel); - tableTables.setRowSorter(activeTablesSorter); + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + + // get table text cell settings + DefaultTableCellRenderer baseRenderer = (DefaultTableCellRenderer) table.getDefaultRenderer(String.class); + JLabel baseComp = (JLabel) baseRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + String skillCode = baseComp.getText(); + + // apply settings to render panel from parent + renderPanel.setOpaque(baseComp.isOpaque()); + renderPanel.setForeground(CardRendererUtils.copyColor(baseComp.getForeground())); + renderPanel.setBackground(CardRendererUtils.copyColor(baseComp.getBackground())); + renderPanel.setBorder(baseComp.getBorder()); + + // create each skill symbol as child label + renderPanel.removeAll(); + renderPanel.setLayout(new BoxLayout(renderPanel, BoxLayout.X_AXIS)); + for (char skillSymbol : skillCode.toCharArray()) { + JLabel symbolLabel = new JLabel(); + symbolLabel.setBorder(new EmptyBorder(0, 3, 0, 0)); + symbolLabel.setIcon(skillIcon); + renderPanel.add(symbolLabel); + } + + return renderPanel; + } + }; + + /** + * Creates new form TablesPanel + */ + public TablesPanel() { + + tableModel = new TableTableModel(); + matchesModel = new MatchesTableModel(); + gameChooser = new GameChooser(); + + initComponents(); + // tableModel.setSession(session); + + // formater + timeFormater.setLocale(Locale.ENGLISH); + JustNow jn = timeFormater.getUnit(JustNow.class); + jn.setMaxQuantity(1000L * 30L); // 30 seconds gap (show "just now" from 0 to 30 secs) + + // 1. TABLE CURRENT + tableTables.createDefaultColumnsFromModel(); + activeTablesSorter = new MageTableRowSorter(tableModel); + tableTables.setRowSorter(activeTablesSorter); + + // time ago + tableTables.getColumnModel().getColumn(TableTableModel.COLUMN_CREATED).setCellRenderer(timeAgoCellRenderer); + // skill level + tableTables.getColumnModel().getColumn(TableTableModel.COLUMN_SKILL).setCellRenderer(skillCellRenderer); - // time ago - tableTables.getColumnModel().getColumn(TableTableModel.COLUMN_CREATED).setCellRenderer(timeAgoCellRenderer); /* date sorter (not need, default is good - see getColumnClass) activeTablesSorter.setComparator(TableTableModel.COLUMN_CREATED, new Comparator() { @Override @@ -166,1579 +196,1610 @@ public class TablesPanel extends javax.swing.JPanel { } });*/ - // default sort by created date (last games from above) - ArrayList list = new ArrayList(); - list.add(new RowSorter.SortKey(TableTableModel.COLUMN_CREATED, SortOrder.DESCENDING)); - activeTablesSorter.setSortKeys(list); - - TableUtil.setColumnWidthAndOrder(tableTables, DEFAULT_COLUMNS_WIDTH, KEY_TABLES_COLUMNS_WIDTH, KEY_TABLES_COLUMNS_ORDER); - - // 2. TABLE COMPLETED - completedTablesSorter = new MageTableRowSorter(matchesModel); - tableCompleted.setRowSorter(completedTablesSorter); - - // duration - tableCompleted.getColumnModel().getColumn(MatchesTableModel.COLUMN_DURATION).setCellRenderer(durationCellRenderer); - // start-end - tableCompleted.getColumnModel().getColumn(MatchesTableModel.COLUMN_START).setCellRenderer(datetimeCellRenderer); - tableCompleted.getColumnModel().getColumn(MatchesTableModel.COLUMN_END).setCellRenderer(datetimeCellRenderer); - // default sort by ended date (last games from above) - ArrayList list2 = new ArrayList(); - list2.add(new RowSorter.SortKey(MatchesTableModel.COLUMN_END, SortOrder.DESCENDING)); - completedTablesSorter.setSortKeys(list2); - - // 3. CHAT - chatPanelMain.getUserChatPanel().useExtendedView(ChatPanelBasic.VIEW_MODE.NONE); - chatPanelMain.getUserChatPanel().setBorder(null); - chatPanelMain.getUserChatPanel().setChatType(ChatPanelBasic.ChatType.TABLES); - - // 4. BUTTONS - filterButtons = new JToggleButton[]{btnStateWaiting, btnStateActive, btnStateFinished, - btnTypeMatch, btnTypeTourneyConstructed, btnTypeTourneyLimited, - btnFormatBlock, btnFormatStandard, btnFormatModern, btnFormatLegacy, btnFormatVintage, btnFormatCommander, btnFormatTinyLeader, btnFormatLimited, btnFormatOther, - btnSkillBeginner, btnSkillCasual, btnSkillSerious, btnRated, btnUnrated, btnOpen, btnPassword}; - - JComponent[] components = new JComponent[]{chatPanelMain, jSplitPane1, jScrollPaneTablesActive, jScrollPaneTablesFinished, jPanelTop, jPanelTables}; - for (JComponent component : components) { - component.setOpaque(false); - } - - jScrollPaneTablesActive.getViewport().setBackground(new Color(255, 255, 255, 50)); - jScrollPaneTablesFinished.getViewport().setBackground(new Color(255, 255, 255, 50)); - - restoreFilters(); - setGUISize(); - - Action openTableAction; - openTableAction = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - int modelRow = Integer.valueOf(e.getActionCommand()); - UUID tableId = (UUID) tableModel.getValueAt(modelRow, TableTableModel.ACTION_COLUMN + 3); - UUID gameId = (UUID) tableModel.getValueAt(modelRow, TableTableModel.ACTION_COLUMN + 2); - String action = (String) tableModel.getValueAt(modelRow, TableTableModel.ACTION_COLUMN); - String deckType = (String) tableModel.getValueAt(modelRow, TableTableModel.COLUMN_DECK_TYPE); - boolean isTournament = (Boolean) tableModel.getValueAt(modelRow, TableTableModel.ACTION_COLUMN + 1); - String owner = (String) tableModel.getValueAt(modelRow, TableTableModel.COLUMN_OWNER); - String pwdColumn = (String) tableModel.getValueAt(modelRow, TableTableModel.COLUMN_PASSWORD); - switch (action) { - case "Join": - if (owner.equals(SessionHandler.getUserName()) || owner.startsWith(SessionHandler.getUserName() + ',')) { - try { - JDesktopPane desktopPane = (JDesktopPane) MageFrame.getUI().getComponent(MageComponents.DESKTOP_PANE); - JInternalFrame[] windows = desktopPane.getAllFramesInLayer(javax.swing.JLayeredPane.DEFAULT_LAYER); - for (JInternalFrame frame : windows) { - if (frame.getTitle().equals("Waiting for players")) { - frame.toFront(); - frame.setVisible(true); - try { - frame.setSelected(true); - } catch (PropertyVetoException ve) { - LOGGER.error(ve); - } - } - - } - } catch (InterruptedException ex) { - LOGGER.error(ex); - } - return; - } - if (isTournament) { - LOGGER.info("Joining tournament " + tableId); - if (deckType.startsWith("Limited")) { - if (TableTableModel.PASSWORD_VALUE_YES.equals(pwdColumn)) { - joinTableDialog.showDialog(roomId, tableId, true, deckType.startsWith("Limited")); - } else { - SessionHandler.joinTournamentTable(roomId, tableId, SessionHandler.getUserName(), PlayerType.HUMAN, 1, null, ""); - } - } else { - joinTableDialog.showDialog(roomId, tableId, true, deckType.startsWith("Limited")); - } - } else { - LOGGER.info("Joining table " + tableId); - joinTableDialog.showDialog(roomId, tableId, false, false); - } - break; - case "Remove": - UserRequestMessage message = new UserRequestMessage("Removing table", "Are you sure you want to remove table?"); - message.setButton1("No", null); - message.setButton2("Yes", PlayerAction.CLIENT_REMOVE_TABLE); - MageFrame.getInstance().showUserRequestDialog(message); - break; - case "Show": - if (isTournament) { - LOGGER.info("Showing tournament table " + tableId); - SessionHandler.watchTable(roomId, tableId); - } - break; - case "Watch": - if (!isTournament) { - LOGGER.info("Watching table " + tableId); - SessionHandler.watchTable(roomId, tableId); - } - break; - case "Replay": - LOGGER.info("Replaying game " + gameId); - SessionHandler.replayGame(gameId); - break; - } - } - }; - - Action closedTableAction; - closedTableAction = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - int modelRow = Integer.valueOf(e.getActionCommand()); - String action = (String) matchesModel.getValueAt(modelRow, MatchesTableModel.COLUMN_ACTION); - switch (action) { - case "Replay": - java.util.List gameList = matchesModel.getListofGames(modelRow); - if (gameList != null && !gameList.isEmpty()) { - if (gameList.size() == 1) { - SessionHandler.replayGame(gameList.get(0)); - } else { - gameChooser.show(gameList, MageFrame.getDesktop().getMousePosition()); - } - } - // MageFrame.getDesktop().showTournament(tournamentId); - break; - case "Show": - if (matchesModel.isTournament(modelRow)) { - LOGGER.info("Showing tournament table " + matchesModel.getTableId(modelRow)); - SessionHandler.watchTable(roomId, matchesModel.getTableId(modelRow)); - } - break; - } - } - }; - - // !!!! adds action buttons to the table panel (don't delete this) - actionButton1 = new ButtonColumn(tableTables, openTableAction, tableTables.convertColumnIndexToView(TableTableModel.ACTION_COLUMN)); - actionButton2 = new ButtonColumn(tableCompleted, closedTableAction, tableCompleted.convertColumnIndexToView(MatchesTableModel.COLUMN_ACTION)); - // !!!! - addTableDoubleClickListener(tableTables, openTableAction); - addTableDoubleClickListener(tableCompleted, closedTableAction); - } - - private void addTableDoubleClickListener(JTable table, Action action) { - table.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - int row = table.convertRowIndexToModel(table.getSelectedRow()); - if (e.getClickCount() == 2 && row != -1) { - action.actionPerformed(new ActionEvent(e.getSource(), e.getID(), "" + row)); - } - } - }); - } - - public void cleanUp() { - saveGuiSettings(); - chatPanelMain.cleanUp(); - } - - public void changeGUISize() { - chatPanelMain.changeGUISize(); - actionButton1.changeGUISize(); - actionButton2.changeGUISize(); - setGUISize(); - } - - private void setGUISize() { - tableTables.getTableHeader().setFont(GUISizeHelper.tableFont); - tableTables.setFont(GUISizeHelper.tableFont); - tableTables.setRowHeight(GUISizeHelper.getTableRowHeight()); - - tableCompleted.getTableHeader().setFont(GUISizeHelper.tableFont); - tableCompleted.setFont(GUISizeHelper.tableFont); - tableCompleted.setRowHeight(GUISizeHelper.getTableRowHeight()); - - jSplitPane1.setDividerSize(GUISizeHelper.dividerBarSize); - jSplitPaneTables.setDividerSize(GUISizeHelper.dividerBarSize); - jScrollPaneTablesActive.getVerticalScrollBar().setPreferredSize(new Dimension(GUISizeHelper.scrollBarSize, 0)); - jScrollPaneTablesActive.getHorizontalScrollBar().setPreferredSize(new Dimension(0, GUISizeHelper.scrollBarSize)); - - ImageIcon icon = new javax.swing.ImageIcon(getClass().getResource("/buttons/state_waiting.png")); - Image img = icon.getImage(); - Image newimg = img.getScaledInstance(GUISizeHelper.menuFont.getSize(), GUISizeHelper.menuFont.getSize(), java.awt.Image.SCALE_SMOOTH); - btnStateWaiting.setIcon(new ImageIcon(newimg)); - - icon = new javax.swing.ImageIcon(getClass().getResource("/buttons/state_active.png")); - img = icon.getImage(); - newimg = img.getScaledInstance(GUISizeHelper.menuFont.getSize(), GUISizeHelper.menuFont.getSize(), java.awt.Image.SCALE_SMOOTH); - btnStateActive.setIcon(new ImageIcon(newimg)); - - icon = new javax.swing.ImageIcon(getClass().getResource("/buttons/state_finished.png")); - img = icon.getImage(); - newimg = img.getScaledInstance(GUISizeHelper.menuFont.getSize(), GUISizeHelper.menuFont.getSize(), java.awt.Image.SCALE_SMOOTH); - btnStateFinished.setIcon(new ImageIcon(newimg)); - - int iconSize = 48 + GUISizeHelper.menuFont.getSize() * 2 - 15; - icon = new javax.swing.ImageIcon(getClass().getResource("/buttons/match_new.png")); - img = icon.getImage(); - newimg = img.getScaledInstance(iconSize, iconSize, java.awt.Image.SCALE_SMOOTH); - btnNewTable.setIcon(new ImageIcon(newimg)); - - icon = new javax.swing.ImageIcon(getClass().getResource("/buttons/tourney_new.png")); - img = icon.getImage(); - newimg = img.getScaledInstance(iconSize, iconSize, java.awt.Image.SCALE_SMOOTH); - btnNewTournament.setIcon(new ImageIcon(newimg)); - - for (JToggleButton component : filterButtons) { - component.setFont(GUISizeHelper.menuFont); - } - Dimension newDimension = new Dimension((int) jPanelBottom.getPreferredSize().getWidth(), GUISizeHelper.menuFont.getSize() + 28); - jPanelBottom.setMinimumSize(newDimension); - jPanelBottom.setPreferredSize(newDimension); - jButtonFooterNext.setFont(GUISizeHelper.menuFont); - jLabelFooterLabel.setFont(new Font(GUISizeHelper.menuFont.getName(), Font.BOLD, GUISizeHelper.menuFont.getSize())); - jLabelFooterText.setFont(GUISizeHelper.menuFont); - } - - private void saveDividerLocations() { - // save divider locations and divider saveDividerLocations - GuiDisplayUtil.saveCurrentBoundsToPrefs(); - GuiDisplayUtil.saveDividerLocationToPrefs(KEY_TABLES_DIVIDER_LOCATION_1, this.jSplitPane1.getDividerLocation()); - GuiDisplayUtil.saveDividerLocationToPrefs(KEY_TABLES_DIVIDER_LOCATION_2, this.jSplitPaneTables.getDividerLocation()); - GuiDisplayUtil.saveDividerLocationToPrefs(KEY_TABLES_DIVIDER_LOCATION_3, chatPanelMain.getSplitDividerLocation()); - } - - private void restoreFilters() { - TableUtil.setActiveFilters(KEY_TABLES_FILTER_SETTINGS, filterButtons); - setTableFilter(); - } - - private void saveGuiSettings() { - TableUtil.saveActiveFiltersToPrefs(KEY_TABLES_FILTER_SETTINGS, filterButtons); - TableUtil.saveColumnWidthAndOrderToPrefs(tableTables, KEY_TABLES_COLUMNS_WIDTH, KEY_TABLES_COLUMNS_ORDER); - } - - private void restoreDividers() { - Rectangle currentBounds = MageFrame.getDesktop().getBounds(); - if (currentBounds != null) { - String firstDivider = PreferencesDialog.getCachedValue(KEY_TABLES_DIVIDER_LOCATION_1, null); - String tableDivider = PreferencesDialog.getCachedValue(KEY_TABLES_DIVIDER_LOCATION_2, null); - String chatDivider = PreferencesDialog.getCachedValue(KEY_TABLES_DIVIDER_LOCATION_3, null); - GuiDisplayUtil.restoreDividerLocations(currentBounds, firstDivider, jSplitPane1); - GuiDisplayUtil.restoreDividerLocations(currentBounds, tableDivider, jSplitPaneTables); - GuiDisplayUtil.restoreDividerLocations(currentBounds, chatDivider, chatPanelMain); - } - } - - public Map getUIComponents() { - Map components = new HashMap<>(); - - components.put("jScrollPane1", jScrollPaneTablesActive); - components.put("jScrollPane1ViewPort", jScrollPaneTablesActive.getViewport()); - components.put("jPanel1", jPanelTop); - components.put("tablesPanel", this); - - return components; - } - - public void updateTables(Collection tables) { - try { - tableModel.loadData(tables); - this.tableTables.repaint(); - } catch (MageRemoteException ex) { - hideTables(); - } - } - - public void updateMatches(Collection matches) { - try { - matchesModel.loadData(matches); - this.tableCompleted.repaint(); - } catch (MageRemoteException ex) { - hideTables(); - } - } - - public void startTasks() { - if (SessionHandler.getSession() != null) { - if (updateTablesTask == null || updateTablesTask.isDone()) { - updateTablesTask = new UpdateTablesTask(roomId, this); - updateTablesTask.execute(); - } - if (updatePlayersTask == null || updatePlayersTask.isDone()) { - updatePlayersTask = new UpdatePlayersTask(roomId, this.chatPanelMain); - updatePlayersTask.execute(); - } - if (this.btnStateFinished.isSelected()) { - if (updateMatchesTask == null || updateMatchesTask.isDone()) { - updateMatchesTask = new UpdateMatchesTask(roomId, this); - updateMatchesTask.execute(); - } - } else if (updateMatchesTask != null) { - updateMatchesTask.cancel(true); - } - } - } - - public void stopTasks() { - if (updateTablesTask != null) { - updateTablesTask.cancel(true); - } - if (updatePlayersTask != null) { - updatePlayersTask.cancel(true); - } - if (updateMatchesTask != null) { - updateMatchesTask.cancel(true); - } - } - - public void showTables(UUID roomId) { - this.roomId = roomId; - UUID chatRoomId = null; - if (SessionHandler.getSession() != null) { - btnQuickStart.setVisible(SessionHandler.isTestMode()); - gameChooser.init(); - chatRoomId = SessionHandler.getRoomChatId(roomId).orElse(null); - } - if (newTableDialog == null) { - newTableDialog = new NewTableDialog(); - MageFrame.getDesktop().add(newTableDialog, JLayeredPane.MODAL_LAYER); - } - if (newTournamentDialog == null) { - newTournamentDialog = new NewTournamentDialog(); - MageFrame.getDesktop().add(newTournamentDialog, JLayeredPane.MODAL_LAYER); - } - if (joinTableDialog == null) { - joinTableDialog = new JoinTableDialog(); - MageFrame.getDesktop().add(joinTableDialog, JLayeredPane.MODAL_LAYER); - } - if (chatRoomId != null) { - this.chatPanelMain.getUserChatPanel().connect(chatRoomId); - startTasks(); - this.setVisible(true); - this.repaint(); - } else { - hideTables(); - } - //tableModel.setSession(session); - - reloadMessages(); - - MageFrame.getUI().addButton(MageComponents.NEW_GAME_BUTTON, btnNewTable); - - // divider locations have to be set with delay else values set are overwritten with system defaults - Executors.newSingleThreadScheduledExecutor().schedule(() -> restoreDividers(), 300, TimeUnit.MILLISECONDS); - - } - - protected void reloadMessages() { - // reload server messages - java.util.List serverMessages = SessionHandler.getServerMessages(); - synchronized (this) { - this.messages = serverMessages; - this.currentMessage = 0; - } - if (serverMessages.isEmpty()) { - this.jPanelBottom.setVisible(false); - } else { - this.jPanelBottom.setVisible(true); - URLHandler.RemoveMouseAdapter(jLabelFooterText); - URLHandler.handleMessage(serverMessages.get(0), this.jLabelFooterText); - this.jButtonFooterNext.setVisible(serverMessages.size() > 1); - } - } - - public void hideTables() { - this.saveDividerLocations(); - for (Component component : MageFrame.getDesktop().getComponents()) { - if (component instanceof TableWaitingDialog) { - ((TableWaitingDialog) component).closeDialog(); - } - } - stopTasks(); - this.chatPanelMain.getUserChatPanel().disconnect(); - - Component c = this.getParent(); - while (c != null && !(c instanceof TablesPane)) { - c = c.getParent(); - } - if (c != null) { - ((TablesPane) c).hideFrame(); - } - } - - public ChatPanelBasic getChatPanel() { - return chatPanelMain.getUserChatPanel(); - } - - public void setTableFilter() { - // state - java.util.List> stateFilterList = new ArrayList<>(); - if (btnStateWaiting.isSelected()) { - stateFilterList.add(RowFilter.regexFilter("Waiting", TableTableModel.COLUMN_STATUS)); - } - if (btnStateActive.isSelected()) { - stateFilterList.add(RowFilter.regexFilter("Dueling|Constructing|Drafting|Sideboard", TableTableModel.COLUMN_STATUS)); - } - - // type - java.util.List> typeFilterList = new ArrayList<>(); - if (btnTypeMatch.isSelected()) { - typeFilterList.add(RowFilter.regexFilter("Two|Commander|Free|Tiny|Momir", TableTableModel.COLUMN_GAME_TYPE)); - } - if (btnTypeTourneyConstructed.isSelected()) { - typeFilterList.add(RowFilter.regexFilter("Constructed", TableTableModel.COLUMN_GAME_TYPE)); - } - if (btnTypeTourneyLimited.isSelected()) { - typeFilterList.add(RowFilter.regexFilter("Booster|Sealed", TableTableModel.COLUMN_GAME_TYPE)); - } - - // format - java.util.List> formatFilterList = new ArrayList<>(); - if (btnFormatBlock.isSelected()) { - formatFilterList.add(RowFilter.regexFilter("^Constructed.*Block", TableTableModel.COLUMN_DECK_TYPE)); - } - if (btnFormatStandard.isSelected()) { - formatFilterList.add(RowFilter.regexFilter("^Constructed - Standard", TableTableModel.COLUMN_DECK_TYPE)); - } - if (btnFormatModern.isSelected()) { - formatFilterList.add(RowFilter.regexFilter("^Constructed - Modern", TableTableModel.COLUMN_DECK_TYPE)); - } - if (btnFormatLegacy.isSelected()) { - formatFilterList.add(RowFilter.regexFilter("^Constructed - Legacy", TableTableModel.COLUMN_DECK_TYPE)); - } - if (btnFormatVintage.isSelected()) { - formatFilterList.add(RowFilter.regexFilter("^Constructed - Vintage", TableTableModel.COLUMN_DECK_TYPE)); - } - if (btnFormatCommander.isSelected()) { - formatFilterList.add(RowFilter.regexFilter("^Commander|^Duel Commander|^Penny Dreadful Commander|^Freeform Commander|^MTGO 1v1 Commander|^Duel Brawl|^Brawl", TableTableModel.COLUMN_DECK_TYPE)); - } - if (btnFormatTinyLeader.isSelected()) { - formatFilterList.add(RowFilter.regexFilter("^Tiny", TableTableModel.COLUMN_DECK_TYPE)); - } - if (btnFormatLimited.isSelected()) { - formatFilterList.add(RowFilter.regexFilter("^Limited", TableTableModel.COLUMN_DECK_TYPE)); - } - if (btnFormatOther.isSelected()) { - formatFilterList.add(RowFilter.regexFilter("^Momir Basic|^Constructed - Pauper|^Constructed - Frontier|^Constructed - Extended|^Constructed - Eternal|^Constructed - Historical|^Constructed - Super|^Constructed - Freeform|^Australian Highlander|^Canadian Highlander|^Constructed - Old", TableTableModel.COLUMN_DECK_TYPE)); - } - - java.util.List> skillFilterList = new ArrayList<>(); - if (btnSkillBeginner.isSelected()) { - skillFilterList.add(RowFilter.regexFilter(SkillLevel.BEGINNER.toString(), TableTableModel.COLUMN_SKILL)); - } - if (btnSkillCasual.isSelected()) { - skillFilterList.add(RowFilter.regexFilter(SkillLevel.CASUAL.toString(), TableTableModel.COLUMN_SKILL)); - } - if (btnSkillSerious.isSelected()) { - skillFilterList.add(RowFilter.regexFilter(SkillLevel.SERIOUS.toString(), TableTableModel.COLUMN_SKILL)); - } - - String ratedMark = TableTableModel.RATED_VALUE_YES; - java.util.List> ratingFilterList = new ArrayList<>(); - if (btnRated.isSelected()) { - // yes word - ratingFilterList.add(RowFilter.regexFilter("^" + ratedMark, TableTableModel.COLUMN_RATING)); - } - if (btnUnrated.isSelected()) { - // not yes word, see https://stackoverflow.com/a/406408/1276632 - ratingFilterList.add(RowFilter.regexFilter("^((?!" + ratedMark + ").)*$", TableTableModel.COLUMN_RATING)); - } - - // Password - String passwordMark = TableTableModel.PASSWORD_VALUE_YES; - java.util.List> passwordFilterList = new ArrayList<>(); - if (btnPassword.isSelected()) { - // yes - passwordFilterList.add(RowFilter.regexFilter("^" + passwordMark, TableTableModel.COLUMN_PASSWORD)); - } - if (btnOpen.isSelected()) { - // no - passwordFilterList.add(RowFilter.regexFilter("^((?!" + passwordMark + ").)*$", TableTableModel.COLUMN_PASSWORD)); - } - - // Hide games of ignored players - java.util.List> ignoreListFilterList = new ArrayList<>(); - String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> ""); - final Set ignoreListCopy = IgnoreList.ignoreList(serverAddress); - if (!ignoreListCopy.isEmpty()) { - ignoreListFilterList.add(new RowFilter() { - @Override - public boolean include(Entry entry) { - final String owner = entry.getStringValue(TableTableModel.COLUMN_OWNER); - return !ignoreListCopy.contains(owner); - } - }); - } - - if (stateFilterList.isEmpty() || typeFilterList.isEmpty() || formatFilterList.isEmpty() - || skillFilterList.isEmpty() || ratingFilterList.isEmpty() - || passwordFilterList.isEmpty()) { // no selection - activeTablesSorter.setRowFilter(RowFilter.regexFilter("Nothing", TableTableModel.COLUMN_SKILL)); - } else { - java.util.List> filterList = new ArrayList<>(); - - if (stateFilterList.size() > 1) { - filterList.add(RowFilter.orFilter(stateFilterList)); - } else if (stateFilterList.size() == 1) { - filterList.addAll(stateFilterList); - } - - if (typeFilterList.size() > 1) { - filterList.add(RowFilter.orFilter(typeFilterList)); - } else if (typeFilterList.size() == 1) { - filterList.addAll(typeFilterList); - } - - if (formatFilterList.size() > 1) { - filterList.add(RowFilter.orFilter(formatFilterList)); - } else if (formatFilterList.size() == 1) { - filterList.addAll(formatFilterList); - } - - if (skillFilterList.size() > 1) { - filterList.add(RowFilter.orFilter(skillFilterList)); - } else if (skillFilterList.size() == 1) { - filterList.addAll(skillFilterList); - } - - if (ratingFilterList.size() > 1) { - filterList.add(RowFilter.orFilter(ratingFilterList)); - } else if (ratingFilterList.size() == 1) { - filterList.addAll(ratingFilterList); - } - - if (passwordFilterList.size() > 1) { - filterList.add(RowFilter.orFilter(passwordFilterList)); - } else if (passwordFilterList.size() == 1) { - filterList.addAll(passwordFilterList); - } - - if (ignoreListFilterList.size() > 1) { - filterList.add(RowFilter.orFilter(ignoreListFilterList)); - } else if (ignoreListFilterList.size() == 1) { - filterList.addAll(ignoreListFilterList); - } - - if (filterList.size() == 1) { - activeTablesSorter.setRowFilter(filterList.get(0)); - } else { - activeTablesSorter.setRowFilter(RowFilter.andFilter(filterList)); - } - } - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - java.awt.GridBagConstraints gridBagConstraints; - - jPanelTop = new javax.swing.JPanel(); - btnNewTable = new javax.swing.JButton(); - btnNewTournament = new javax.swing.JButton(); - filterBar1 = new javax.swing.JToolBar(); - btnStateWaiting = new javax.swing.JToggleButton(); - btnStateActive = new javax.swing.JToggleButton(); - btnStateFinished = new javax.swing.JToggleButton(); - jSeparator1 = new javax.swing.JToolBar.Separator(); - btnTypeMatch = new javax.swing.JToggleButton(); - btnTypeTourneyConstructed = new javax.swing.JToggleButton(); - btnTypeTourneyLimited = new javax.swing.JToggleButton(); - jSeparator4 = new javax.swing.JToolBar.Separator(); - btnSkillBeginner = new javax.swing.JToggleButton(); - btnSkillCasual = new javax.swing.JToggleButton(); - btnSkillSerious = new javax.swing.JToggleButton(); - jSeparator5 = new javax.swing.JToolBar.Separator(); - btnRated = new javax.swing.JToggleButton(); - btnUnrated = new javax.swing.JToggleButton(); - filterBar2 = new javax.swing.JToolBar(); - btnFormatBlock = new javax.swing.JToggleButton(); - btnFormatStandard = new javax.swing.JToggleButton(); - btnFormatModern = new javax.swing.JToggleButton(); - btnFormatLegacy = new javax.swing.JToggleButton(); - btnFormatVintage = new javax.swing.JToggleButton(); - jSeparator3 = new javax.swing.JToolBar.Separator(); - btnFormatCommander = new javax.swing.JToggleButton(); - btnFormatTinyLeader = new javax.swing.JToggleButton(); - jSeparator2 = new javax.swing.JToolBar.Separator(); - btnFormatLimited = new javax.swing.JToggleButton(); - btnFormatOther = new javax.swing.JToggleButton(); - jSeparator5 = new javax.swing.JToolBar.Separator(); - btnOpen = new javax.swing.JToggleButton(); - btnPassword = new javax.swing.JToggleButton(); - btnQuickStart = new javax.swing.JButton(); - jSplitPane1 = new javax.swing.JSplitPane(); - jPanelTables = new javax.swing.JPanel(); - jSplitPaneTables = new javax.swing.JSplitPane(); - jScrollPaneTablesActive = new javax.swing.JScrollPane(); - tableTables = new javax.swing.JTable(); - jScrollPaneTablesFinished = new javax.swing.JScrollPane(); - tableCompleted = new javax.swing.JTable(); - chatPanelMain = new mage.client.table.PlayersChatPanel(); - jPanelBottom = new javax.swing.JPanel(); - jButtonFooterNext = new javax.swing.JButton(); - jLabelFooterLabel = new javax.swing.JLabel(); - jLabelFooterText = new javax.swing.JLabel(); - - setLayout(new java.awt.GridBagLayout()); - - jPanelTop.setBackground(java.awt.Color.white); - jPanelTop.setOpaque(false); - - btnNewTable.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/match_new.png"))); // NOI18N - btnNewTable.setToolTipText("Creates a new match table."); - btnNewTable.setMargin(new java.awt.Insets(2, 2, 2, 2)); - btnNewTable.addActionListener(evt -> btnNewTableActionPerformed(evt)); - - btnNewTournament.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/tourney_new.png"))); // NOI18N - btnNewTournament.setToolTipText("Creates a new tourney table."); - btnNewTournament.setMargin(new java.awt.Insets(2, 2, 2, 2)); - btnNewTournament.addActionListener(evt -> btnNewTournamentActionPerformed(evt)); - - filterBar1.setFloatable(false); - filterBar1.setForeground(new java.awt.Color(102, 102, 255)); - filterBar1.setFocusable(false); - filterBar1.setOpaque(false); - - btnStateWaiting.setSelected(true); - btnStateWaiting.setToolTipText("Shows all tables waiting for players."); - btnStateWaiting.setActionCommand("stateWait"); - btnStateWaiting.setFocusPainted(false); - btnStateWaiting.setFocusable(false); - btnStateWaiting.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnStateWaiting.setRequestFocusEnabled(false); - btnStateWaiting.setVerifyInputWhenFocusTarget(false); - btnStateWaiting.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnStateWaiting.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar1.add(btnStateWaiting); - - btnStateActive.setSelected(true); - btnStateActive.setToolTipText("Shows all tables with active matches."); - btnStateActive.setActionCommand("stateActive"); - btnStateActive.setFocusPainted(false); - btnStateActive.setFocusable(false); - btnStateActive.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnStateActive.setRequestFocusEnabled(false); - btnStateActive.setVerifyInputWhenFocusTarget(false); - btnStateActive.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnStateActive.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar1.add(btnStateActive); - - btnStateFinished.setSelected(true); - btnStateFinished.setToolTipText("Toggles the visibility of the table of completed
matches and tournaments in the lower area.\n
Showing the last 50 finished matches."); - btnStateFinished.setActionCommand("stateFinished"); - btnStateFinished.setFocusPainted(false); - btnStateFinished.setFocusable(false); - btnStateFinished.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnStateFinished.setRequestFocusEnabled(false); - btnStateFinished.setVerifyInputWhenFocusTarget(false); - btnStateFinished.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnStateFinished.addActionListener(evt -> btnStateFinishedActionPerformed(evt)); - filterBar1.add(btnStateFinished); - filterBar1.add(jSeparator1); - - btnTypeMatch.setSelected(true); - btnTypeMatch.setText("Matches"); - btnTypeMatch.setToolTipText("Shows all non tournament tables."); - btnTypeMatch.setActionCommand("typeMatch"); - btnTypeMatch.setFocusPainted(false); - btnTypeMatch.setFocusable(false); - btnTypeMatch.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnTypeMatch.setRequestFocusEnabled(false); - btnTypeMatch.setVerifyInputWhenFocusTarget(false); - btnTypeMatch.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnTypeMatch.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar1.add(btnTypeMatch); - - btnTypeTourneyConstructed.setSelected(true); - btnTypeTourneyConstructed.setText("Constructed tourn."); - btnTypeTourneyConstructed.setToolTipText("Shows all constructed tournament tables."); - btnTypeTourneyConstructed.setActionCommand("typeTourneyConstructed"); - btnTypeTourneyConstructed.setFocusPainted(false); - btnTypeTourneyConstructed.setFocusable(false); - btnTypeTourneyConstructed.setRequestFocusEnabled(false); - btnTypeTourneyConstructed.setVerifyInputWhenFocusTarget(false); - btnTypeTourneyConstructed.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar1.add(btnTypeTourneyConstructed); - - btnTypeTourneyLimited.setSelected(true); - btnTypeTourneyLimited.setText("Limited tourn."); - btnTypeTourneyLimited.setToolTipText("Shows all limited tournament tables."); - btnTypeTourneyLimited.setActionCommand("typeTourneyLimited"); - btnTypeTourneyLimited.setFocusPainted(false); - btnTypeTourneyLimited.setFocusable(false); - btnTypeTourneyLimited.setMaximumSize(new java.awt.Dimension(72, 20)); - btnTypeTourneyLimited.setRequestFocusEnabled(false); - btnTypeTourneyLimited.setVerifyInputWhenFocusTarget(false); - btnTypeTourneyLimited.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar1.add(btnTypeTourneyLimited); - filterBar1.add(jSeparator4); - - btnSkillBeginner.setSelected(true); - btnSkillBeginner.setText("Beginner"); - btnSkillBeginner.setToolTipText("Shows all tables with skill level beginner."); - btnSkillBeginner.setActionCommand("typeMatch"); - btnSkillBeginner.setFocusPainted(false); - btnSkillBeginner.setFocusable(false); - btnSkillBeginner.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnSkillBeginner.setRequestFocusEnabled(false); - btnSkillBeginner.setVerifyInputWhenFocusTarget(false); - btnSkillBeginner.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnSkillBeginner.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar1.add(btnSkillBeginner); - - btnSkillCasual.setSelected(true); - btnSkillCasual.setText("Casual"); - btnSkillCasual.setToolTipText("Shows all tables with skill level casual."); - btnSkillCasual.setActionCommand("typeMatch"); - btnSkillCasual.setFocusPainted(false); - btnSkillCasual.setFocusable(false); - btnSkillCasual.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnSkillCasual.setRequestFocusEnabled(false); - btnSkillCasual.setVerifyInputWhenFocusTarget(false); - btnSkillCasual.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnSkillCasual.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar1.add(btnSkillCasual); - - btnSkillSerious.setSelected(true); - btnSkillSerious.setText("Serious"); - btnSkillSerious.setToolTipText("Shows all tables with skill level serious."); - btnSkillSerious.setActionCommand("typeMatch"); - btnSkillSerious.setFocusPainted(false); - btnSkillSerious.setFocusable(false); - btnSkillSerious.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnSkillSerious.setRequestFocusEnabled(false); - btnSkillSerious.setVerifyInputWhenFocusTarget(false); - btnSkillSerious.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnSkillSerious.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar1.add(btnSkillSerious); - - filterBar1.add(jSeparator4); - - btnRated.setSelected(true); - btnRated.setText("Rated"); - btnRated.setToolTipText("Shows all rated tables."); - btnRated.setFocusPainted(false); - btnRated.setFocusable(false); - btnRated.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnRated.setRequestFocusEnabled(false); - btnRated.setVerifyInputWhenFocusTarget(false); - btnRated.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnRated.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar1.add(btnRated); - - btnUnrated.setSelected(true); - btnUnrated.setText("Unrated"); - btnUnrated.setToolTipText("Shows all unrated tables."); - btnUnrated.setFocusPainted(false); - btnUnrated.setFocusable(false); - btnUnrated.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnUnrated.setRequestFocusEnabled(false); - btnUnrated.setVerifyInputWhenFocusTarget(false); - btnUnrated.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnUnrated.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar1.add(btnUnrated); - - // second filter line - filterBar2.setFloatable(false); - filterBar2.setFocusable(false); - filterBar2.setOpaque(false); - - btnFormatBlock.setSelected(true); - btnFormatBlock.setText("Block"); - btnFormatBlock.setToolTipText("Block constructed formats."); - btnFormatBlock.setFocusPainted(false); - btnFormatBlock.setFocusable(false); - btnFormatBlock.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnFormatBlock.setRequestFocusEnabled(false); - btnFormatBlock.setVerifyInputWhenFocusTarget(false); - btnFormatBlock.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnFormatBlock.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar2.add(btnFormatBlock); - - btnFormatStandard.setSelected(true); - btnFormatStandard.setText("Standard"); - btnFormatStandard.setToolTipText("Standard format."); - btnFormatStandard.setFocusPainted(false); - btnFormatStandard.setFocusable(false); - btnFormatStandard.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnFormatStandard.setRequestFocusEnabled(false); - btnFormatStandard.setVerifyInputWhenFocusTarget(false); - btnFormatStandard.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnFormatStandard.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar2.add(btnFormatStandard); - - btnFormatModern.setSelected(true); - btnFormatModern.setText("Modern"); - btnFormatModern.setToolTipText("Modern format."); - btnFormatModern.setFocusPainted(false); - btnFormatModern.setFocusable(false); - btnFormatModern.setRequestFocusEnabled(false); - btnFormatModern.setVerifyInputWhenFocusTarget(false); - btnFormatModern.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar2.add(btnFormatModern); - - btnFormatLegacy.setSelected(true); - btnFormatLegacy.setText("Legacy"); - btnFormatLegacy.setToolTipText("Legacy format."); - btnFormatLegacy.setFocusPainted(false); - btnFormatLegacy.setFocusable(false); - btnFormatLegacy.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnFormatLegacy.setRequestFocusEnabled(false); - btnFormatLegacy.setVerifyInputWhenFocusTarget(false); - btnFormatLegacy.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnFormatLegacy.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar2.add(btnFormatLegacy); - - btnFormatVintage.setSelected(true); - btnFormatVintage.setText("Vintage"); - btnFormatVintage.setToolTipText("Vintage format."); - btnFormatVintage.setFocusPainted(false); - btnFormatVintage.setFocusable(false); - btnFormatVintage.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnFormatVintage.setRequestFocusEnabled(false); - btnFormatVintage.setVerifyInputWhenFocusTarget(false); - btnFormatVintage.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnFormatVintage.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar2.add(btnFormatVintage); - filterBar2.add(jSeparator3); - - btnFormatCommander.setSelected(true); - btnFormatCommander.setText("Commander"); - btnFormatCommander.setToolTipText("Commander format."); - btnFormatCommander.setFocusPainted(false); - btnFormatCommander.setFocusable(false); - btnFormatCommander.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnFormatCommander.setRequestFocusEnabled(false); - btnFormatCommander.setVerifyInputWhenFocusTarget(false); - btnFormatCommander.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnFormatCommander.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar2.add(btnFormatCommander); - - btnFormatTinyLeader.setSelected(true); - btnFormatTinyLeader.setText("Tiny Leader"); - btnFormatTinyLeader.setToolTipText("Tiny Leader format."); - btnFormatTinyLeader.setFocusPainted(false); - btnFormatTinyLeader.setFocusable(false); - btnFormatTinyLeader.setRequestFocusEnabled(false); - btnFormatTinyLeader.setVerifyInputWhenFocusTarget(false); - btnFormatTinyLeader.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar2.add(btnFormatTinyLeader); - filterBar2.add(jSeparator2); - - btnFormatLimited.setSelected(true); - btnFormatLimited.setText("Limited"); - btnFormatLimited.setToolTipText("Limited format."); - btnFormatLimited.setFocusPainted(false); - btnFormatLimited.setFocusable(false); - btnFormatLimited.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnFormatLimited.setRequestFocusEnabled(false); - btnFormatLimited.setVerifyInputWhenFocusTarget(false); - btnFormatLimited.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnFormatLimited.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar2.add(btnFormatLimited); - - btnFormatOther.setSelected(true); - btnFormatOther.setText("Other"); - btnFormatOther.setToolTipText("Other formats (Freeform, Pauper, Extended, etc.)"); - btnFormatOther.setFocusPainted(false); - btnFormatOther.setFocusable(false); - btnFormatOther.setRequestFocusEnabled(false); - btnFormatOther.setVerifyInputWhenFocusTarget(false); - btnFormatOther.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar2.add(btnFormatOther); - filterBar2.add(jSeparator5); - - btnOpen.setSelected(true); - btnOpen.setText("Open"); - btnOpen.setToolTipText("Show open games"); - btnOpen.setFocusPainted(false); - btnOpen.setFocusable(false); - btnOpen.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnOpen.setRequestFocusEnabled(false); - btnOpen.setVerifyInputWhenFocusTarget(false); - btnOpen.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnOpen.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar2.add(btnOpen); - - btnPassword.setSelected(true); - btnPassword.setText("PW"); - btnPassword.setToolTipText("Show passworded games"); - btnPassword.setFocusPainted(false); - btnPassword.setFocusable(false); - btnPassword.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnPassword.setRequestFocusEnabled(false); - btnPassword.setVerifyInputWhenFocusTarget(false); - btnPassword.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnPassword.addActionListener(evt -> btnFilterActionPerformed(evt)); - filterBar2.add(btnPassword); - - btnQuickStart.setText("Quick Start"); - btnQuickStart.setFocusable(false); - btnQuickStart.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - btnQuickStart.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - btnQuickStart.addActionListener(evt -> btnQuickStartActionPerformed(evt)); - - javax.swing.GroupLayout jPanelTopLayout = new javax.swing.GroupLayout(jPanelTop); - jPanelTop.setLayout(jPanelTopLayout); - jPanelTopLayout.setHorizontalGroup( - jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanelTopLayout.createSequentialGroup() - .addContainerGap() - .addComponent(btnNewTable) - .addGap(6, 6, 6) - .addComponent(btnNewTournament) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(filterBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 491, Short.MAX_VALUE) - .addComponent(filterBar2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnQuickStart) - .addContainerGap(835, Short.MAX_VALUE)) - ); - jPanelTopLayout.setVerticalGroup( - jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanelTopLayout.createSequentialGroup() - .addContainerGap() - .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(btnNewTable) - .addComponent(btnNewTournament)) - .addGroup(jPanelTopLayout.createSequentialGroup() - .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(filterBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(btnQuickStart)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(filterBar2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) - .addContainerGap()) - ); - - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; - add(jPanelTop, gridBagConstraints); - - jSplitPane1.setBorder(null); - jSplitPane1.setDividerSize(10); - jSplitPane1.setResizeWeight(1.0); - - jSplitPaneTables.setBorder(null); - jSplitPaneTables.setDividerSize(10); - jSplitPaneTables.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); - jSplitPaneTables.setResizeWeight(0.5); - - jScrollPaneTablesActive.setBorder(null); - jScrollPaneTablesActive.setViewportBorder(null); - - tableTables.setModel(this.tableModel); - jScrollPaneTablesActive.setViewportView(tableTables); - - jSplitPaneTables.setLeftComponent(jScrollPaneTablesActive); - - jScrollPaneTablesFinished.setBorder(null); - jScrollPaneTablesFinished.setViewportBorder(null); - jScrollPaneTablesFinished.setMinimumSize(new java.awt.Dimension(23, 0)); - - tableCompleted.setModel(this.matchesModel); - jScrollPaneTablesFinished.setViewportView(tableCompleted); - - jSplitPaneTables.setRightComponent(jScrollPaneTablesFinished); - - javax.swing.GroupLayout jPanelTablesLayout = new javax.swing.GroupLayout(jPanelTables); - jPanelTables.setLayout(jPanelTablesLayout); - jPanelTablesLayout.setHorizontalGroup( - jPanelTablesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jSplitPaneTables, javax.swing.GroupLayout.PREFERRED_SIZE, 23, Short.MAX_VALUE) - ); - jPanelTablesLayout.setVerticalGroup( - jPanelTablesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jSplitPaneTables, javax.swing.GroupLayout.DEFAULT_SIZE, 672, Short.MAX_VALUE) - ); - - jSplitPane1.setLeftComponent(jPanelTables); - jSplitPane1.setRightComponent(chatPanelMain); - - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridy = 1; - gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; - gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.weighty = 1.0; - add(jSplitPane1, gridBagConstraints); - - jPanelBottom.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); - jPanelBottom.setPreferredSize(new java.awt.Dimension(516, 37)); - jPanelBottom.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT)); - - jButtonFooterNext.setText("Next"); - jButtonFooterNext.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - jButtonFooterNext.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButtonFooterNext.setOpaque(false); - jButtonFooterNext.addActionListener(evt -> jButtonFooterNextActionPerformed(evt)); - jPanelBottom.add(jButtonFooterNext); - - jLabelFooterLabel.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N - jLabelFooterLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - jLabelFooterLabel.setText("Message of the Day:"); - jLabelFooterLabel.setAlignmentY(0.3F); - jPanelBottom.add(jLabelFooterLabel); - - jLabelFooterText.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - jLabelFooterText.setText("You are playing Mage version 0.7.5. Welcome! -- Mage dev team --"); - jPanelBottom.add(jLabelFooterText); - - gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridy = 2; - gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; - add(jPanelBottom, gridBagConstraints); - }//
//GEN-END:initComponents - - private void btnNewTournamentActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnNewTournamentActionPerformed - newTournamentDialog.showDialog(roomId); -}//GEN-LAST:event_btnNewTournamentActionPerformed - - private void btnQuickStartActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuickStartActionPerformed - TableView table; - try { - File f = new File("test.dck"); - if (!f.exists()) { - JOptionPane.showMessageDialog(null, "Couldn't find test.dck file for quick game start", "Error", JOptionPane.ERROR_MESSAGE); - return; - } - - MatchOptions options = new MatchOptions("1", "Two Player Duel", false, 2); - options.getPlayerTypes().add(PlayerType.HUMAN); - options.getPlayerTypes().add(PlayerType.COMPUTER_MAD); - options.setDeckType("Limited"); - options.setAttackOption(MultiplayerAttackOption.LEFT); - options.setRange(RangeOfInfluence.ALL); - options.setWinsNeeded(1); - options.setMatchTimeLimit(MatchTimeLimit.NONE); - options.setFreeMulligans(2); - options.setSkillLevel(SkillLevel.CASUAL); - options.setRollbackTurnsAllowed(true); - options.setQuitRatio(100); - String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> ""); - options.setBannedUsers(IgnoreList.ignoreList(serverAddress)); - table = SessionHandler.createTable(roomId, options); - - SessionHandler.joinTable(roomId, table.getTableId(), "Human", PlayerType.HUMAN, 1, DeckImporterUtil.importDeck("test.dck"), ""); - SessionHandler.joinTable(roomId, table.getTableId(), "Computer", PlayerType.COMPUTER_MAD, 5, DeckImporterUtil.importDeck("test.dck"), ""); - SessionHandler.startMatch(roomId, table.getTableId()); - } catch (HeadlessException ex) { - handleError(ex); - } -}//GEN-LAST:event_btnQuickStartActionPerformed - - private void btnNewTableActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnNewTableActionPerformed - newTableDialog.showDialog(roomId); - }//GEN-LAST:event_btnNewTableActionPerformed - - private void jButtonFooterNextActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonFooterNextActionPerformed - synchronized (this) { - if (messages != null && !messages.isEmpty()) { - currentMessage++; - if (currentMessage >= messages.size()) { - currentMessage = 0; - } - - URLHandler.RemoveMouseAdapter(jLabelFooterText); - URLHandler.handleMessage(messages.get(currentMessage), this.jLabelFooterText); - } - } - }//GEN-LAST:event_jButtonFooterNextActionPerformed - - private void btnFilterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFilterActionPerformed - setTableFilter(); - }//GEN-LAST:event_btnFilterActionPerformed - - private void btnStateFinishedActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnStateFinishedActionPerformed - if (this.btnStateFinished.isSelected()) { - this.jSplitPaneTables.setDividerLocation(-1); - } else { - this.jSplitPaneTables.setDividerLocation(this.jPanelTables.getHeight()); - } - this.startTasks(); - }//GEN-LAST:event_btnStateFinishedActionPerformed - - private void handleError(Exception ex) { - LOGGER.fatal("Error loading deck: ", ex); - JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Error loading deck.", "Error", JOptionPane.ERROR_MESSAGE); - } - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JToggleButton btnFormatBlock; - private javax.swing.JToggleButton btnFormatCommander; - private javax.swing.JToggleButton btnFormatLegacy; - private javax.swing.JToggleButton btnFormatLimited; - private javax.swing.JToggleButton btnFormatModern; - private javax.swing.JToggleButton btnFormatOther; - private javax.swing.JToggleButton btnFormatStandard; - private javax.swing.JToggleButton btnFormatTinyLeader; - private javax.swing.JToggleButton btnFormatVintage; - private javax.swing.JButton btnNewTable; - private javax.swing.JButton btnNewTournament; - private javax.swing.JToggleButton btnOpen; - private javax.swing.JToggleButton btnPassword; - private javax.swing.JButton btnQuickStart; - private javax.swing.JToggleButton btnSkillBeginner; - private javax.swing.JToggleButton btnSkillCasual; - private javax.swing.JToggleButton btnSkillSerious; - private javax.swing.JToggleButton btnRated; - private javax.swing.JToggleButton btnUnrated; - private javax.swing.JToggleButton btnStateActive; - private javax.swing.JToggleButton btnStateFinished; - private javax.swing.JToggleButton btnStateWaiting; - private javax.swing.JToggleButton btnTypeMatch; - private javax.swing.JToggleButton btnTypeTourneyConstructed; - private javax.swing.JToggleButton btnTypeTourneyLimited; - private mage.client.table.PlayersChatPanel chatPanelMain; - private javax.swing.JToolBar filterBar1; - private javax.swing.JToolBar filterBar2; - private javax.swing.JButton jButtonFooterNext; - private javax.swing.JLabel jLabelFooterLabel; - private javax.swing.JLabel jLabelFooterText; - private javax.swing.JPanel jPanelBottom; - private javax.swing.JPanel jPanelTables; - private javax.swing.JPanel jPanelTop; - private javax.swing.JScrollPane jScrollPaneTablesActive; - private javax.swing.JScrollPane jScrollPaneTablesFinished; - private javax.swing.JToolBar.Separator jSeparator1; - private javax.swing.JToolBar.Separator jSeparator2; - private javax.swing.JToolBar.Separator jSeparator3; - private javax.swing.JToolBar.Separator jSeparator4; - private javax.swing.JToolBar.Separator jSeparator5; - private javax.swing.JSplitPane jSplitPane1; - private javax.swing.JSplitPane jSplitPaneTables; - private javax.swing.JTable tableCompleted; - private javax.swing.JTable tableTables; - // End of variables declaration//GEN-END:variables - -} - -class TableTableModel extends AbstractTableModel { - - final ImageIcon tourneyIcon = new javax.swing.ImageIcon(getClass().getResource("/tables/tourney_icon.png")); - final ImageIcon matchIcon = new javax.swing.ImageIcon(getClass().getResource("/tables/match_icon.png")); - - public static final int COLUMN_ICON = 0; - public static final int COLUMN_DECK_TYPE = 1; // column the deck type is located (starting with 0) Start string is used to check for Limited - public static final int COLUMN_OWNER = 2; - public static final int COLUMN_GAME_TYPE = 3; - public static final int COLUMN_INFO = 4; - public static final int COLUMN_STATUS = 5; - public static final int COLUMN_PASSWORD = 6; - public static final int COLUMN_CREATED = 7; - public static final int COLUMN_SKILL = 8; - public static final int COLUMN_RATING = 9; - public static final int COLUMN_QUIT_RATIO = 10; - public static final int ACTION_COLUMN = 11; // column the action is located (starting with 0) - - public static final String RATED_VALUE_YES = "YES"; - public static final String RATED_VALUE_NO = ""; - - public static final String PASSWORD_VALUE_YES = "YES"; - - private final String[] columnNames = new String[]{"M/T", "Deck Type", "Owner / Players", "Game Type", "Info", "Status", "Password", "Created / Started", "Skill Level", "Rating", "Quit %", "Action"}; - - private TableView[] tables = new TableView[0]; - - TableTableModel() { - } - - public void loadData(Collection tables) throws MageRemoteException { - this.tables = tables.toArray(new TableView[0]); - this.fireTableDataChanged(); - } - - @Override - public int getRowCount() { - return tables.length; - } - - @Override - public int getColumnCount() { - return columnNames.length; - } - - @Override - public Object getValueAt(int arg0, int arg1) { - switch (arg1) { - case 0: - return tables[arg0].isTournament() ? tourneyIcon : matchIcon; - case 1: - return tables[arg0].getDeckType(); - case 2: - return tables[arg0].getControllerName(); - case 3: - return tables[arg0].getGameType(); - case 4: - return tables[arg0].getAdditionalInfo(); - case 5: - return tables[arg0].getTableStateText(); - case 6: - return tables[arg0].isPassworded() ? PASSWORD_VALUE_YES : ""; - case 7: - return tables[arg0].getCreateTime(); // use cell render, not format here - case 8: - return tables[arg0].getSkillLevel(); - case 9: - return tables[arg0].isRated() ? RATED_VALUE_YES : RATED_VALUE_NO; - case 10: - return tables[arg0].getQuitRatio(); - case 11: - switch (tables[arg0].getTableState()) { - - case WAITING: - String owner = tables[arg0].getControllerName(); - if (SessionHandler.getSession() != null && owner.equals(SessionHandler.getUserName())) { - return ""; - } - return "Join"; - case CONSTRUCTING: - case DRAFTING: - if (tables[arg0].isTournament()) { - return "Show"; - } - case DUELING: - if (tables[arg0].isTournament()) { - return "Show"; - } else { - owner = tables[arg0].getControllerName(); - if (SessionHandler.getSession() != null && owner.equals(SessionHandler.getUserName())) { - return ""; - } - if (tables[arg0].getSpectatorsAllowed()) { - return "Watch"; - } - return ""; - } - default: - return ""; - } - case 12: - return tables[arg0].isTournament(); - case 13: - if (!tables[arg0].getGames().isEmpty()) { - return tables[arg0].getGames().get(0); - } - return null; - case 14: - return tables[arg0].getTableId(); - } - return ""; - } - - @Override - public String getColumnName(int columnIndex) { - String colName = ""; - - if (columnIndex <= getColumnCount()) { - colName = columnNames[columnIndex]; - } - - return colName; - } - - @Override - public Class getColumnClass(int columnIndex) { - switch (columnIndex) { - case COLUMN_ICON: - return Icon.class; - case COLUMN_SKILL: - return SkillLevel.class; - case COLUMN_CREATED: - return Date.class; - default: - return String.class; - } - } - - @Override - public boolean isCellEditable(int rowIndex, int columnIndex) { - return columnIndex == ACTION_COLUMN; - } - -} - -class UpdateTablesTask extends SwingWorker> { - - private final UUID roomId; - private final TablesPanel panel; - - private static final Logger logger = Logger.getLogger(UpdateTablesTask.class); - - private int count = 0; - - UpdateTablesTask(UUID roomId, TablesPanel panel) { - - this.roomId = roomId; - this.panel = panel; - } - - @Override - protected Void doInBackground() throws Exception { - while (!isCancelled()) { - Collection tables = SessionHandler.getTables(roomId); - if (tables != null) { - this.publish(tables); - } - TimeUnit.SECONDS.sleep(3); - } - return null; - } - - @Override - protected void process(java.util.List> view) { - panel.updateTables(view.get(0)); - count++; - if (count > 60) { - count = 0; - panel.reloadMessages(); - } - } - - @Override - protected void done() { - try { - get(); - } catch (InterruptedException | ExecutionException ex) { - logger.fatal("Update Tables Task error", ex); - } catch (CancellationException ex) { - } - } - -} - -class UpdatePlayersTask extends SwingWorker> { - - private final UUID roomId; - private final PlayersChatPanel chat; - - private static final Logger logger = Logger.getLogger(UpdatePlayersTask.class); - - UpdatePlayersTask(UUID roomId, PlayersChatPanel chat) { - - this.roomId = roomId; - this.chat = chat; - } - - @Override - protected Void doInBackground() throws Exception { - while (!isCancelled()) { - this.publish(SessionHandler.getRoomUsers(roomId)); - TimeUnit.SECONDS.sleep(3); - } - return null; - } - - @Override - protected void process(java.util.List> roomUserInfo) { - chat.setRoomUserInfo(roomUserInfo); - } - - @Override - protected void done() { - try { - get(); - } catch (InterruptedException | ExecutionException ex) { - logger.fatal("Update Players Task error", ex); - } catch (CancellationException ex) { - } - } - -} - -class MatchesTableModel extends AbstractTableModel { - - private final String[] columnNames = new String[]{"Deck Type", "Players", "Game Type", "Rating", "Result", "Duration", "Start Time", "End Time", "Action"}; - public static final int COLUMN_DURATION = 5; - public static final int COLUMN_START = 6; - public static final int COLUMN_END = 7; - public static final int COLUMN_ACTION = 8; // column the action is located (starting with 0) - - private MatchView[] matches = new MatchView[0]; - - public void loadData(Collection matches) throws MageRemoteException { - this.matches = matches.toArray(new MatchView[0]); - this.fireTableDataChanged(); - } - - MatchesTableModel() { - } - - @Override - public int getRowCount() { - return matches.length; - } - - @Override - public int getColumnCount() { - return columnNames.length; - } - - @Override - public Object getValueAt(int arg0, int arg1) { - switch (arg1) { - case 0: - return matches[arg0].getDeckType(); - case 1: - return matches[arg0].getPlayers(); - case 2: - return matches[arg0].getGameType(); - case 3: - return matches[arg0].isRated() ? TableTableModel.RATED_VALUE_YES : TableTableModel.RATED_VALUE_NO; - case 4: - return matches[arg0].getResult(); - case 5: - if (matches[arg0].getEndTime() != null) { - return matches[arg0].getEndTime().getTime() - matches[arg0].getStartTime().getTime() + new Date().getTime(); - } else { - return 0L; - } - case 6: - return matches[arg0].getStartTime(); - case 7: - return matches[arg0].getEndTime(); - case 8: - if (matches[arg0].isTournament()) { - return "Show"; - } else if (matches[arg0].isReplayAvailable()) { - return "Replay"; - } else { - return "None"; - } - case 9: - return matches[arg0].getGames(); - } - return ""; - } - - public java.util.List getListofGames(int row) { - return matches[row].getGames(); - } - - public boolean isTournament(int row) { - return matches[row].isTournament(); - } - - public UUID getMatchId(int row) { - return matches[row].getMatchId(); - } - - public UUID getTableId(int row) { - return matches[row].getTableId(); - } - - @Override - public String getColumnName(int columnIndex) { - String colName = ""; - - if (columnIndex <= getColumnCount()) { - colName = columnNames[columnIndex]; - } - - return colName; - } - - @Override - public Class getColumnClass(int columnIndex) { - switch (columnIndex) { - case COLUMN_DURATION: - return Long.class; - case COLUMN_START: - return Date.class; - case COLUMN_END: - return Date.class; - default: - return String.class; - } - } - - @Override - public boolean isCellEditable(int rowIndex, int columnIndex) { - return columnIndex == COLUMN_ACTION; - } - -} - -class UpdateMatchesTask extends SwingWorker> { - - private final UUID roomId; - private final TablesPanel panel; - - private static final Logger logger = Logger.getLogger(UpdateTablesTask.class); - - UpdateMatchesTask(UUID roomId, TablesPanel panel) { - this.roomId = roomId; - this.panel = panel; - } - - @Override - protected Void doInBackground() throws Exception { - while (!isCancelled()) { - Collection matches = SessionHandler.getFinishedMatches(roomId); - if (!matches.isEmpty()) { - this.publish(matches); - } - TimeUnit.SECONDS.sleep(10); - } - return null; - } - - @Override - protected void process(java.util.List> view) { - panel.updateMatches(view.get(0)); - } - - @Override - protected void done() { - try { - get(); - } catch (InterruptedException | ExecutionException ex) { - logger.fatal("Update Matches Task error", ex); - } catch (CancellationException ex) { - } - } - -} - -class GameChooser extends JPopupMenu { - - public void init() { - - } - - public void show(java.util.List games, Point p) { - if (p == null) { - return; - } - this.removeAll(); - for (UUID gameId : games) { - this.add(new GameChooserAction(gameId, gameId.toString())); - } - this.show(MageFrame.getDesktop(), p.x, p.y); - GuiDisplayUtil.keepComponentInsideScreen(p.x, p.y, this); - } - - private class GameChooserAction extends AbstractAction { - - private final UUID id; - - public GameChooserAction(UUID id, String choice) { - this.id = id; - putValue(Action.NAME, choice); - } - - @Override - public void actionPerformed(ActionEvent e) { - SessionHandler.replayGame(id); - setVisible(false); - } - - } - -} + // default sort by created date (last games from above) + ArrayList list = new ArrayList(); + list.add(new RowSorter.SortKey(TableTableModel.COLUMN_CREATED, SortOrder.DESCENDING)); + activeTablesSorter.setSortKeys(list); + + TableUtil.setColumnWidthAndOrder(tableTables, DEFAULT_COLUMNS_WIDTH, KEY_TABLES_COLUMNS_WIDTH, KEY_TABLES_COLUMNS_ORDER); + + // 2. TABLE COMPLETED + completedTablesSorter = new MageTableRowSorter(matchesModel); + tableCompleted.setRowSorter(completedTablesSorter); + + // duration + tableCompleted.getColumnModel().getColumn(MatchesTableModel.COLUMN_DURATION).setCellRenderer(durationCellRenderer); + // start-end + tableCompleted.getColumnModel().getColumn(MatchesTableModel.COLUMN_START).setCellRenderer(datetimeCellRenderer); + tableCompleted.getColumnModel().getColumn(MatchesTableModel.COLUMN_END).setCellRenderer(datetimeCellRenderer); + // default sort by ended date (last games from above) + ArrayList list2 = new ArrayList(); + list2.add(new RowSorter.SortKey(MatchesTableModel.COLUMN_END, SortOrder.DESCENDING)); + completedTablesSorter.setSortKeys(list2); + + // 3. CHAT + chatPanelMain.getUserChatPanel().useExtendedView(ChatPanelBasic.VIEW_MODE.NONE); + chatPanelMain.getUserChatPanel().setBorder(null); + chatPanelMain.getUserChatPanel().setChatType(ChatPanelBasic.ChatType.TABLES); + + // 4. BUTTONS + filterButtons = new JToggleButton[]{btnStateWaiting, btnStateActive, btnStateFinished, + btnTypeMatch, btnTypeTourneyConstructed, btnTypeTourneyLimited, + btnFormatBlock, btnFormatStandard, btnFormatModern, btnFormatLegacy, btnFormatVintage, btnFormatCommander, btnFormatTinyLeader, btnFormatLimited, btnFormatOther, + btnSkillBeginner, btnSkillCasual, btnSkillSerious, btnRated, btnUnrated, btnOpen, btnPassword}; + + JComponent[] components = new JComponent[]{chatPanelMain, jSplitPane1, jScrollPaneTablesActive, jScrollPaneTablesFinished, jPanelTop, jPanelTables}; + for (JComponent component : components) { + component.setOpaque(false); + } + + jScrollPaneTablesActive.getViewport().setBackground(new Color(255, 255, 255, 50)); + jScrollPaneTablesFinished.getViewport().setBackground(new Color(255, 255, 255, 50)); + + restoreFilters(); + setGUISize(); + + Action openTableAction; + openTableAction = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + int modelRow = Integer.valueOf(e.getActionCommand()); + UUID tableId = (UUID) tableModel.getValueAt(modelRow, TableTableModel.ACTION_COLUMN + 3); + UUID gameId = (UUID) tableModel.getValueAt(modelRow, TableTableModel.ACTION_COLUMN + 2); + String action = (String) tableModel.getValueAt(modelRow, TableTableModel.ACTION_COLUMN); + String deckType = (String) tableModel.getValueAt(modelRow, TableTableModel.COLUMN_DECK_TYPE); + boolean isTournament = (Boolean) tableModel.getValueAt(modelRow, TableTableModel.ACTION_COLUMN + 1); + String owner = (String) tableModel.getValueAt(modelRow, TableTableModel.COLUMN_OWNER); + String pwdColumn = (String) tableModel.getValueAt(modelRow, TableTableModel.COLUMN_PASSWORD); + switch (action) { + case "Join": + if (owner.equals(SessionHandler.getUserName()) || owner.startsWith(SessionHandler.getUserName() + ',')) { + try { + JDesktopPane desktopPane = (JDesktopPane) MageFrame.getUI().getComponent(MageComponents.DESKTOP_PANE); + JInternalFrame[] windows = desktopPane.getAllFramesInLayer(javax.swing.JLayeredPane.DEFAULT_LAYER); + for (JInternalFrame frame : windows) { + if (frame.getTitle().equals("Waiting for players")) { + frame.toFront(); + frame.setVisible(true); + try { + frame.setSelected(true); + } catch (PropertyVetoException ve) { + LOGGER.error(ve); + } + } + + } + } catch (InterruptedException ex) { + LOGGER.error(ex); + } + return; + } + if (isTournament) { + LOGGER.info("Joining tournament " + tableId); + if (deckType.startsWith("Limited")) { + if (TableTableModel.PASSWORD_VALUE_YES.equals(pwdColumn)) { + joinTableDialog.showDialog(roomId, tableId, true, deckType.startsWith("Limited")); + } else { + SessionHandler.joinTournamentTable(roomId, tableId, SessionHandler.getUserName(), PlayerType.HUMAN, 1, null, ""); + } + } else { + joinTableDialog.showDialog(roomId, tableId, true, deckType.startsWith("Limited")); + } + } else { + LOGGER.info("Joining table " + tableId); + joinTableDialog.showDialog(roomId, tableId, false, false); + } + break; + case "Remove": + UserRequestMessage message = new UserRequestMessage("Removing table", "Are you sure you want to remove table?"); + message.setButton1("No", null); + message.setButton2("Yes", PlayerAction.CLIENT_REMOVE_TABLE); + MageFrame.getInstance().showUserRequestDialog(message); + break; + case "Show": + if (isTournament) { + LOGGER.info("Showing tournament table " + tableId); + SessionHandler.watchTable(roomId, tableId); + } + break; + case "Watch": + if (!isTournament) { + LOGGER.info("Watching table " + tableId); + SessionHandler.watchTable(roomId, tableId); + } + break; + case "Replay": + LOGGER.info("Replaying game " + gameId); + SessionHandler.replayGame(gameId); + break; + } + } + }; + + Action closedTableAction; + closedTableAction = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + int modelRow = Integer.valueOf(e.getActionCommand()); + String action = (String) matchesModel.getValueAt(modelRow, MatchesTableModel.COLUMN_ACTION); + switch (action) { + case "Replay": + java.util.List gameList = matchesModel.getListofGames(modelRow); + if (gameList != null && !gameList.isEmpty()) { + if (gameList.size() == 1) { + SessionHandler.replayGame(gameList.get(0)); + } else { + gameChooser.show(gameList, MageFrame.getDesktop().getMousePosition()); + } + } + // MageFrame.getDesktop().showTournament(tournamentId); + break; + case "Show": + if (matchesModel.isTournament(modelRow)) { + LOGGER.info("Showing tournament table " + matchesModel.getTableId(modelRow)); + SessionHandler.watchTable(roomId, matchesModel.getTableId(modelRow)); + } + break; + } + } + }; + + // !!!! adds action buttons to the table panel (don't delete this) + actionButton1 = new ButtonColumn(tableTables, openTableAction, tableTables.convertColumnIndexToView(TableTableModel.ACTION_COLUMN)); + actionButton2 = new ButtonColumn(tableCompleted, closedTableAction, tableCompleted.convertColumnIndexToView(MatchesTableModel.COLUMN_ACTION)); + // !!!! + addTableDoubleClickListener(tableTables, openTableAction); + addTableDoubleClickListener(tableCompleted, closedTableAction); + } + + private void addTableDoubleClickListener(JTable table, Action action) { + table.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + int selRow = table.getSelectedRow(); + if (selRow != -1) { + int dataRow = table.convertRowIndexToModel(selRow); + if (dataRow != -1) { + action.actionPerformed(new ActionEvent(e.getSource(), e.getID(), "" + dataRow)); + } + } + } + } + }); + } + + public void cleanUp() { + saveGuiSettings(); + chatPanelMain.cleanUp(); + } + + public void changeGUISize() { + chatPanelMain.changeGUISize(); + actionButton1.changeGUISize(); + actionButton2.changeGUISize(); + setGUISize(); + } + + private void setGUISize() { + tableTables.getTableHeader().setFont(GUISizeHelper.tableFont); + tableTables.setFont(GUISizeHelper.tableFont); + tableTables.setRowHeight(GUISizeHelper.getTableRowHeight()); + + tableCompleted.getTableHeader().setFont(GUISizeHelper.tableFont); + tableCompleted.setFont(GUISizeHelper.tableFont); + tableCompleted.setRowHeight(GUISizeHelper.getTableRowHeight()); + + jSplitPane1.setDividerSize(GUISizeHelper.dividerBarSize); + jSplitPaneTables.setDividerSize(GUISizeHelper.dividerBarSize); + jScrollPaneTablesActive.getVerticalScrollBar().setPreferredSize(new Dimension(GUISizeHelper.scrollBarSize, 0)); + jScrollPaneTablesActive.getHorizontalScrollBar().setPreferredSize(new Dimension(0, GUISizeHelper.scrollBarSize)); + + ImageIcon icon = new javax.swing.ImageIcon(getClass().getResource("/buttons/state_waiting.png")); + Image img = icon.getImage(); + Image newimg = img.getScaledInstance(GUISizeHelper.menuFont.getSize(), GUISizeHelper.menuFont.getSize(), java.awt.Image.SCALE_SMOOTH); + btnStateWaiting.setIcon(new ImageIcon(newimg)); + + icon = new javax.swing.ImageIcon(getClass().getResource("/buttons/state_active.png")); + img = icon.getImage(); + newimg = img.getScaledInstance(GUISizeHelper.menuFont.getSize(), GUISizeHelper.menuFont.getSize(), java.awt.Image.SCALE_SMOOTH); + btnStateActive.setIcon(new ImageIcon(newimg)); + + icon = new javax.swing.ImageIcon(getClass().getResource("/buttons/state_finished.png")); + img = icon.getImage(); + newimg = img.getScaledInstance(GUISizeHelper.menuFont.getSize(), GUISizeHelper.menuFont.getSize(), java.awt.Image.SCALE_SMOOTH); + btnStateFinished.setIcon(new ImageIcon(newimg)); + + int iconSize = 48 + GUISizeHelper.menuFont.getSize() * 2 - 15; + icon = new javax.swing.ImageIcon(getClass().getResource("/buttons/match_new.png")); + img = icon.getImage(); + newimg = img.getScaledInstance(iconSize, iconSize, java.awt.Image.SCALE_SMOOTH); + btnNewTable.setIcon(new ImageIcon(newimg)); + + icon = new javax.swing.ImageIcon(getClass().getResource("/buttons/tourney_new.png")); + img = icon.getImage(); + newimg = img.getScaledInstance(iconSize, iconSize, java.awt.Image.SCALE_SMOOTH); + btnNewTournament.setIcon(new ImageIcon(newimg)); + + for (JToggleButton component : filterButtons) { + component.setFont(GUISizeHelper.menuFont); + } + Dimension newDimension = new Dimension((int) jPanelBottom.getPreferredSize().getWidth(), GUISizeHelper.menuFont.getSize() + 28); + jPanelBottom.setMinimumSize(newDimension); + jPanelBottom.setPreferredSize(newDimension); + jButtonFooterNext.setFont(GUISizeHelper.menuFont); + jLabelFooterLabel.setFont(new Font(GUISizeHelper.menuFont.getName(), Font.BOLD, GUISizeHelper.menuFont.getSize())); + jLabelFooterText.setFont(GUISizeHelper.menuFont); + } + + private void saveDividerLocations() { + // save divider locations and divider saveDividerLocations + GuiDisplayUtil.saveCurrentBoundsToPrefs(); + GuiDisplayUtil.saveDividerLocationToPrefs(KEY_TABLES_DIVIDER_LOCATION_1, this.jSplitPane1.getDividerLocation()); + GuiDisplayUtil.saveDividerLocationToPrefs(KEY_TABLES_DIVIDER_LOCATION_2, this.jSplitPaneTables.getDividerLocation()); + GuiDisplayUtil.saveDividerLocationToPrefs(KEY_TABLES_DIVIDER_LOCATION_3, chatPanelMain.getSplitDividerLocation()); + } + + private void restoreFilters() { + TableUtil.setActiveFilters(KEY_TABLES_FILTER_SETTINGS, filterButtons); + setTableFilter(); + } + + private void saveGuiSettings() { + TableUtil.saveActiveFiltersToPrefs(KEY_TABLES_FILTER_SETTINGS, filterButtons); + TableUtil.saveColumnWidthAndOrderToPrefs(tableTables, KEY_TABLES_COLUMNS_WIDTH, KEY_TABLES_COLUMNS_ORDER); + } + + private void restoreDividers() { + Rectangle currentBounds = MageFrame.getDesktop().getBounds(); + if (currentBounds != null) { + String firstDivider = PreferencesDialog.getCachedValue(KEY_TABLES_DIVIDER_LOCATION_1, null); + String tableDivider = PreferencesDialog.getCachedValue(KEY_TABLES_DIVIDER_LOCATION_2, null); + String chatDivider = PreferencesDialog.getCachedValue(KEY_TABLES_DIVIDER_LOCATION_3, null); + GuiDisplayUtil.restoreDividerLocations(currentBounds, firstDivider, jSplitPane1); + GuiDisplayUtil.restoreDividerLocations(currentBounds, tableDivider, jSplitPaneTables); + GuiDisplayUtil.restoreDividerLocations(currentBounds, chatDivider, chatPanelMain); + } + } + + public Map getUIComponents() { + Map components = new HashMap<>(); + + components.put("jScrollPane1", jScrollPaneTablesActive); + components.put("jScrollPane1ViewPort", jScrollPaneTablesActive.getViewport()); + components.put("jPanel1", jPanelTop); + components.put("tablesPanel", this); + + return components; + } + + public void updateTables(Collection tables) { + try { + tableModel.loadData(tables); + this.tableTables.repaint(); + } catch (MageRemoteException ex) { + hideTables(); + } + } + + public void updateMatches(Collection matches) { + try { + matchesModel.loadData(matches); + this.tableCompleted.repaint(); + } catch (MageRemoteException ex) { + hideTables(); + } + } + + public void startTasks() { + if (SessionHandler.getSession() != null) { + if (updateTablesTask == null || updateTablesTask.isDone()) { + updateTablesTask = new UpdateTablesTask(roomId, this); + updateTablesTask.execute(); + } + if (updatePlayersTask == null || updatePlayersTask.isDone()) { + updatePlayersTask = new UpdatePlayersTask(roomId, this.chatPanelMain); + updatePlayersTask.execute(); + } + if (this.btnStateFinished.isSelected()) { + if (updateMatchesTask == null || updateMatchesTask.isDone()) { + updateMatchesTask = new UpdateMatchesTask(roomId, this); + updateMatchesTask.execute(); + } + } else if (updateMatchesTask != null) { + updateMatchesTask.cancel(true); + } + } + } + + public void stopTasks() { + if (updateTablesTask != null) { + updateTablesTask.cancel(true); + } + if (updatePlayersTask != null) { + updatePlayersTask.cancel(true); + } + if (updateMatchesTask != null) { + updateMatchesTask.cancel(true); + } + } + + public void showTables(UUID roomId) { + this.roomId = roomId; + UUID chatRoomId = null; + if (SessionHandler.getSession() != null) { + btnQuickStart.setVisible(SessionHandler.isTestMode()); + gameChooser.init(); + chatRoomId = SessionHandler.getRoomChatId(roomId).orElse(null); + } + if (newTableDialog == null) { + newTableDialog = new NewTableDialog(); + MageFrame.getDesktop().add(newTableDialog, JLayeredPane.MODAL_LAYER); + } + if (newTournamentDialog == null) { + newTournamentDialog = new NewTournamentDialog(); + MageFrame.getDesktop().add(newTournamentDialog, JLayeredPane.MODAL_LAYER); + } + if (joinTableDialog == null) { + joinTableDialog = new JoinTableDialog(); + MageFrame.getDesktop().add(joinTableDialog, JLayeredPane.MODAL_LAYER); + } + if (chatRoomId != null) { + this.chatPanelMain.getUserChatPanel().connect(chatRoomId); + startTasks(); + this.setVisible(true); + this.repaint(); + } else { + hideTables(); + } + //tableModel.setSession(session); + + reloadMessages(); + + MageFrame.getUI().addButton(MageComponents.NEW_GAME_BUTTON, btnNewTable); + + // divider locations have to be set with delay else values set are overwritten with system defaults + Executors.newSingleThreadScheduledExecutor().schedule(() -> restoreDividers(), 300, TimeUnit.MILLISECONDS); + + } + + protected void reloadMessages() { + // reload server messages + java.util.List serverMessages = SessionHandler.getServerMessages(); + synchronized (this) { + this.messages = serverMessages; + this.currentMessage = 0; + } + if (serverMessages.isEmpty()) { + this.jPanelBottom.setVisible(false); + } else { + this.jPanelBottom.setVisible(true); + URLHandler.RemoveMouseAdapter(jLabelFooterText); + URLHandler.handleMessage(serverMessages.get(0), this.jLabelFooterText); + this.jButtonFooterNext.setVisible(serverMessages.size() > 1); + } + } + + public void hideTables() { + this.saveDividerLocations(); + for (Component component : MageFrame.getDesktop().getComponents()) { + if (component instanceof TableWaitingDialog) { + ((TableWaitingDialog) component).closeDialog(); + } + } + stopTasks(); + this.chatPanelMain.getUserChatPanel().disconnect(); + + Component c = this.getParent(); + while (c != null && !(c instanceof TablesPane)) { + c = c.getParent(); + } + if (c != null) { + ((TablesPane) c).hideFrame(); + } + } + + public ChatPanelBasic getChatPanel() { + return chatPanelMain.getUserChatPanel(); + } + + public void setTableFilter() { + // state + java.util.List> stateFilterList = new ArrayList<>(); + if (btnStateWaiting.isSelected()) { + stateFilterList.add(RowFilter.regexFilter("Waiting", TableTableModel.COLUMN_STATUS)); + } + if (btnStateActive.isSelected()) { + stateFilterList.add(RowFilter.regexFilter("Dueling|Constructing|Drafting|Sideboard", TableTableModel.COLUMN_STATUS)); + } + + // type + java.util.List> typeFilterList = new ArrayList<>(); + if (btnTypeMatch.isSelected()) { + typeFilterList.add(RowFilter.regexFilter("Two|Commander|Free|Tiny|Momir", TableTableModel.COLUMN_GAME_TYPE)); + } + if (btnTypeTourneyConstructed.isSelected()) { + typeFilterList.add(RowFilter.regexFilter("Constructed", TableTableModel.COLUMN_GAME_TYPE)); + } + if (btnTypeTourneyLimited.isSelected()) { + typeFilterList.add(RowFilter.regexFilter("Booster|Sealed", TableTableModel.COLUMN_GAME_TYPE)); + } + + // format + java.util.List> formatFilterList = new ArrayList<>(); + if (btnFormatBlock.isSelected()) { + formatFilterList.add(RowFilter.regexFilter("^Constructed.*Block", TableTableModel.COLUMN_DECK_TYPE)); + } + if (btnFormatStandard.isSelected()) { + formatFilterList.add(RowFilter.regexFilter("^Constructed - Standard", TableTableModel.COLUMN_DECK_TYPE)); + } + if (btnFormatModern.isSelected()) { + formatFilterList.add(RowFilter.regexFilter("^Constructed - Modern", TableTableModel.COLUMN_DECK_TYPE)); + } + if (btnFormatLegacy.isSelected()) { + formatFilterList.add(RowFilter.regexFilter("^Constructed - Legacy", TableTableModel.COLUMN_DECK_TYPE)); + } + if (btnFormatVintage.isSelected()) { + formatFilterList.add(RowFilter.regexFilter("^Constructed - Vintage", TableTableModel.COLUMN_DECK_TYPE)); + } + if (btnFormatCommander.isSelected()) { + formatFilterList.add(RowFilter.regexFilter("^Commander|^Duel Commander|^Penny Dreadful Commander|^Freeform Commander|^MTGO 1v1 Commander|^Duel Brawl|^Brawl", TableTableModel.COLUMN_DECK_TYPE)); + } + if (btnFormatTinyLeader.isSelected()) { + formatFilterList.add(RowFilter.regexFilter("^Tiny", TableTableModel.COLUMN_DECK_TYPE)); + } + if (btnFormatLimited.isSelected()) { + formatFilterList.add(RowFilter.regexFilter("^Limited", TableTableModel.COLUMN_DECK_TYPE)); + } + if (btnFormatOther.isSelected()) { + formatFilterList.add(RowFilter.regexFilter("^Momir Basic|^Constructed - Pauper|^Constructed - Frontier|^Constructed - Extended|^Constructed - Eternal|^Constructed - Historical|^Constructed - Super|^Constructed - Freeform|^Australian Highlander|^Canadian Highlander|^Constructed - Old", TableTableModel.COLUMN_DECK_TYPE)); + } + + // skill + java.util.List> skillFilterList = new ArrayList<>(); + if (btnSkillBeginner.isSelected()) { + skillFilterList.add(RowFilter.regexFilter(this.tableModel.getSkillLevelAsCode(SkillLevel.BEGINNER, true), TableTableModel.COLUMN_SKILL)); + } + if (btnSkillCasual.isSelected()) { + skillFilterList.add(RowFilter.regexFilter(this.tableModel.getSkillLevelAsCode(SkillLevel.CASUAL, true), TableTableModel.COLUMN_SKILL)); + } + if (btnSkillSerious.isSelected()) { + skillFilterList.add(RowFilter.regexFilter(this.tableModel.getSkillLevelAsCode(SkillLevel.SERIOUS, true), TableTableModel.COLUMN_SKILL)); + } + + String ratedMark = TableTableModel.RATED_VALUE_YES; + java.util.List> ratingFilterList = new ArrayList<>(); + if (btnRated.isSelected()) { + // yes word + ratingFilterList.add(RowFilter.regexFilter("^" + ratedMark, TableTableModel.COLUMN_RATING)); + } + if (btnUnrated.isSelected()) { + // not yes word, see https://stackoverflow.com/a/406408/1276632 + ratingFilterList.add(RowFilter.regexFilter("^((?!" + ratedMark + ").)*$", TableTableModel.COLUMN_RATING)); + } + + // Password + String passwordMark = TableTableModel.PASSWORD_VALUE_YES; + java.util.List> passwordFilterList = new ArrayList<>(); + if (btnPassword.isSelected()) { + // yes + passwordFilterList.add(RowFilter.regexFilter("^" + passwordMark, TableTableModel.COLUMN_PASSWORD)); + } + if (btnOpen.isSelected()) { + // no + passwordFilterList.add(RowFilter.regexFilter("^((?!" + passwordMark + ").)*$", TableTableModel.COLUMN_PASSWORD)); + } + + // Hide games of ignored players + java.util.List> ignoreListFilterList = new ArrayList<>(); + String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> ""); + final Set ignoreListCopy = IgnoreList.ignoreList(serverAddress); + if (!ignoreListCopy.isEmpty()) { + ignoreListFilterList.add(new RowFilter() { + @Override + public boolean include(Entry entry) { + final String owner = entry.getStringValue(TableTableModel.COLUMN_OWNER); + return !ignoreListCopy.contains(owner); + } + }); + } + + if (stateFilterList.isEmpty() || typeFilterList.isEmpty() || formatFilterList.isEmpty() + || skillFilterList.isEmpty() || ratingFilterList.isEmpty() + || passwordFilterList.isEmpty()) { // no selection + activeTablesSorter.setRowFilter(RowFilter.regexFilter("Nothing", TableTableModel.COLUMN_SKILL)); + } else { + java.util.List> filterList = new ArrayList<>(); + + if (stateFilterList.size() > 1) { + filterList.add(RowFilter.orFilter(stateFilterList)); + } else if (stateFilterList.size() == 1) { + filterList.addAll(stateFilterList); + } + + if (typeFilterList.size() > 1) { + filterList.add(RowFilter.orFilter(typeFilterList)); + } else if (typeFilterList.size() == 1) { + filterList.addAll(typeFilterList); + } + + if (formatFilterList.size() > 1) { + filterList.add(RowFilter.orFilter(formatFilterList)); + } else if (formatFilterList.size() == 1) { + filterList.addAll(formatFilterList); + } + + if (skillFilterList.size() > 1) { + filterList.add(RowFilter.orFilter(skillFilterList)); + } else if (skillFilterList.size() == 1) { + filterList.addAll(skillFilterList); + } + + if (ratingFilterList.size() > 1) { + filterList.add(RowFilter.orFilter(ratingFilterList)); + } else if (ratingFilterList.size() == 1) { + filterList.addAll(ratingFilterList); + } + + if (passwordFilterList.size() > 1) { + filterList.add(RowFilter.orFilter(passwordFilterList)); + } else if (passwordFilterList.size() == 1) { + filterList.addAll(passwordFilterList); + } + + if (ignoreListFilterList.size() > 1) { + filterList.add(RowFilter.orFilter(ignoreListFilterList)); + } else if (ignoreListFilterList.size() == 1) { + filterList.addAll(ignoreListFilterList); + } + + if (filterList.size() == 1) { + activeTablesSorter.setRowFilter(filterList.get(0)); + } else { + activeTablesSorter.setRowFilter(RowFilter.andFilter(filterList)); + } + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + jPanelTop = new javax.swing.JPanel(); + btnNewTable = new javax.swing.JButton(); + btnNewTournament = new javax.swing.JButton(); + filterBar1 = new javax.swing.JToolBar(); + btnStateWaiting = new javax.swing.JToggleButton(); + btnStateActive = new javax.swing.JToggleButton(); + btnStateFinished = new javax.swing.JToggleButton(); + jSeparator1 = new javax.swing.JToolBar.Separator(); + btnTypeMatch = new javax.swing.JToggleButton(); + btnTypeTourneyConstructed = new javax.swing.JToggleButton(); + btnTypeTourneyLimited = new javax.swing.JToggleButton(); + jSeparator4 = new javax.swing.JToolBar.Separator(); + btnSkillBeginner = new javax.swing.JToggleButton(); + btnSkillCasual = new javax.swing.JToggleButton(); + btnSkillSerious = new javax.swing.JToggleButton(); + jSeparator5 = new javax.swing.JToolBar.Separator(); + btnRated = new javax.swing.JToggleButton(); + btnUnrated = new javax.swing.JToggleButton(); + filterBar2 = new javax.swing.JToolBar(); + btnFormatBlock = new javax.swing.JToggleButton(); + btnFormatStandard = new javax.swing.JToggleButton(); + btnFormatModern = new javax.swing.JToggleButton(); + btnFormatLegacy = new javax.swing.JToggleButton(); + btnFormatVintage = new javax.swing.JToggleButton(); + jSeparator3 = new javax.swing.JToolBar.Separator(); + btnFormatCommander = new javax.swing.JToggleButton(); + btnFormatTinyLeader = new javax.swing.JToggleButton(); + jSeparator2 = new javax.swing.JToolBar.Separator(); + btnFormatLimited = new javax.swing.JToggleButton(); + btnFormatOther = new javax.swing.JToggleButton(); + jSeparator5 = new javax.swing.JToolBar.Separator(); + btnOpen = new javax.swing.JToggleButton(); + btnPassword = new javax.swing.JToggleButton(); + btnQuickStart = new javax.swing.JButton(); + jSplitPane1 = new javax.swing.JSplitPane(); + jPanelTables = new javax.swing.JPanel(); + jSplitPaneTables = new javax.swing.JSplitPane(); + jScrollPaneTablesActive = new javax.swing.JScrollPane(); + tableTables = new javax.swing.JTable(); + jScrollPaneTablesFinished = new javax.swing.JScrollPane(); + tableCompleted = new javax.swing.JTable(); + chatPanelMain = new mage.client.table.PlayersChatPanel(); + jPanelBottom = new javax.swing.JPanel(); + jButtonFooterNext = new javax.swing.JButton(); + jLabelFooterLabel = new javax.swing.JLabel(); + jLabelFooterText = new javax.swing.JLabel(); + + setLayout(new java.awt.GridBagLayout()); + + jPanelTop.setBackground(java.awt.Color.white); + jPanelTop.setOpaque(false); + + btnNewTable.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/match_new.png"))); // NOI18N + btnNewTable.setToolTipText("Creates a new match table."); + btnNewTable.setMargin(new java.awt.Insets(2, 2, 2, 2)); + btnNewTable.addActionListener(evt -> btnNewTableActionPerformed(evt)); + + btnNewTournament.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/tourney_new.png"))); // NOI18N + btnNewTournament.setToolTipText("Creates a new tourney table."); + btnNewTournament.setMargin(new java.awt.Insets(2, 2, 2, 2)); + btnNewTournament.addActionListener(evt -> btnNewTournamentActionPerformed(evt)); + + filterBar1.setFloatable(false); + filterBar1.setForeground(new java.awt.Color(102, 102, 255)); + filterBar1.setFocusable(false); + filterBar1.setOpaque(false); + + btnStateWaiting.setSelected(true); + btnStateWaiting.setToolTipText("Shows all tables waiting for players."); + btnStateWaiting.setActionCommand("stateWait"); + btnStateWaiting.setFocusPainted(false); + btnStateWaiting.setFocusable(false); + btnStateWaiting.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnStateWaiting.setRequestFocusEnabled(false); + btnStateWaiting.setVerifyInputWhenFocusTarget(false); + btnStateWaiting.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnStateWaiting.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar1.add(btnStateWaiting); + + btnStateActive.setSelected(true); + btnStateActive.setToolTipText("Shows all tables with active matches."); + btnStateActive.setActionCommand("stateActive"); + btnStateActive.setFocusPainted(false); + btnStateActive.setFocusable(false); + btnStateActive.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnStateActive.setRequestFocusEnabled(false); + btnStateActive.setVerifyInputWhenFocusTarget(false); + btnStateActive.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnStateActive.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar1.add(btnStateActive); + + btnStateFinished.setSelected(true); + btnStateFinished.setToolTipText("Toggles the visibility of the table of completed
matches and tournaments in the lower area.\n
Showing the last 50 finished matches."); + btnStateFinished.setActionCommand("stateFinished"); + btnStateFinished.setFocusPainted(false); + btnStateFinished.setFocusable(false); + btnStateFinished.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnStateFinished.setRequestFocusEnabled(false); + btnStateFinished.setVerifyInputWhenFocusTarget(false); + btnStateFinished.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnStateFinished.addActionListener(evt -> btnStateFinishedActionPerformed(evt)); + filterBar1.add(btnStateFinished); + filterBar1.add(jSeparator1); + + btnTypeMatch.setSelected(true); + btnTypeMatch.setText("Matches"); + btnTypeMatch.setToolTipText("Shows all non tournament tables."); + btnTypeMatch.setActionCommand("typeMatch"); + btnTypeMatch.setFocusPainted(false); + btnTypeMatch.setFocusable(false); + btnTypeMatch.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnTypeMatch.setRequestFocusEnabled(false); + btnTypeMatch.setVerifyInputWhenFocusTarget(false); + btnTypeMatch.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnTypeMatch.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar1.add(btnTypeMatch); + + btnTypeTourneyConstructed.setSelected(true); + btnTypeTourneyConstructed.setText("Constructed tourn."); + btnTypeTourneyConstructed.setToolTipText("Shows all constructed tournament tables."); + btnTypeTourneyConstructed.setActionCommand("typeTourneyConstructed"); + btnTypeTourneyConstructed.setFocusPainted(false); + btnTypeTourneyConstructed.setFocusable(false); + btnTypeTourneyConstructed.setRequestFocusEnabled(false); + btnTypeTourneyConstructed.setVerifyInputWhenFocusTarget(false); + btnTypeTourneyConstructed.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar1.add(btnTypeTourneyConstructed); + + btnTypeTourneyLimited.setSelected(true); + btnTypeTourneyLimited.setText("Limited tourn."); + btnTypeTourneyLimited.setToolTipText("Shows all limited tournament tables."); + btnTypeTourneyLimited.setActionCommand("typeTourneyLimited"); + btnTypeTourneyLimited.setFocusPainted(false); + btnTypeTourneyLimited.setFocusable(false); + btnTypeTourneyLimited.setMaximumSize(new java.awt.Dimension(72, 20)); + btnTypeTourneyLimited.setRequestFocusEnabled(false); + btnTypeTourneyLimited.setVerifyInputWhenFocusTarget(false); + btnTypeTourneyLimited.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar1.add(btnTypeTourneyLimited); + filterBar1.add(jSeparator4); + + btnSkillBeginner.setSelected(true); + btnSkillBeginner.setText("Beginner"); + btnSkillBeginner.setToolTipText("Shows all tables with skill level beginner."); + btnSkillBeginner.setActionCommand("typeMatch"); + btnSkillBeginner.setFocusPainted(false); + btnSkillBeginner.setFocusable(false); + btnSkillBeginner.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnSkillBeginner.setRequestFocusEnabled(false); + btnSkillBeginner.setVerifyInputWhenFocusTarget(false); + btnSkillBeginner.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnSkillBeginner.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar1.add(btnSkillBeginner); + + btnSkillCasual.setSelected(true); + btnSkillCasual.setText("Casual"); + btnSkillCasual.setToolTipText("Shows all tables with skill level casual."); + btnSkillCasual.setActionCommand("typeMatch"); + btnSkillCasual.setFocusPainted(false); + btnSkillCasual.setFocusable(false); + btnSkillCasual.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnSkillCasual.setRequestFocusEnabled(false); + btnSkillCasual.setVerifyInputWhenFocusTarget(false); + btnSkillCasual.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnSkillCasual.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar1.add(btnSkillCasual); + + btnSkillSerious.setSelected(true); + btnSkillSerious.setText("Serious"); + btnSkillSerious.setToolTipText("Shows all tables with skill level serious."); + btnSkillSerious.setActionCommand("typeMatch"); + btnSkillSerious.setFocusPainted(false); + btnSkillSerious.setFocusable(false); + btnSkillSerious.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnSkillSerious.setRequestFocusEnabled(false); + btnSkillSerious.setVerifyInputWhenFocusTarget(false); + btnSkillSerious.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnSkillSerious.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar1.add(btnSkillSerious); + + filterBar1.add(jSeparator4); + + btnRated.setSelected(true); + btnRated.setText("Rated"); + btnRated.setToolTipText("Shows all rated tables."); + btnRated.setFocusPainted(false); + btnRated.setFocusable(false); + btnRated.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnRated.setRequestFocusEnabled(false); + btnRated.setVerifyInputWhenFocusTarget(false); + btnRated.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnRated.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar1.add(btnRated); + + btnUnrated.setSelected(true); + btnUnrated.setText("Unrated"); + btnUnrated.setToolTipText("Shows all unrated tables."); + btnUnrated.setFocusPainted(false); + btnUnrated.setFocusable(false); + btnUnrated.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnUnrated.setRequestFocusEnabled(false); + btnUnrated.setVerifyInputWhenFocusTarget(false); + btnUnrated.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnUnrated.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar1.add(btnUnrated); + + // second filter line + filterBar2.setFloatable(false); + filterBar2.setFocusable(false); + filterBar2.setOpaque(false); + + btnFormatBlock.setSelected(true); + btnFormatBlock.setText("Block"); + btnFormatBlock.setToolTipText("Block constructed formats."); + btnFormatBlock.setFocusPainted(false); + btnFormatBlock.setFocusable(false); + btnFormatBlock.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnFormatBlock.setRequestFocusEnabled(false); + btnFormatBlock.setVerifyInputWhenFocusTarget(false); + btnFormatBlock.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnFormatBlock.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar2.add(btnFormatBlock); + + btnFormatStandard.setSelected(true); + btnFormatStandard.setText("Standard"); + btnFormatStandard.setToolTipText("Standard format."); + btnFormatStandard.setFocusPainted(false); + btnFormatStandard.setFocusable(false); + btnFormatStandard.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnFormatStandard.setRequestFocusEnabled(false); + btnFormatStandard.setVerifyInputWhenFocusTarget(false); + btnFormatStandard.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnFormatStandard.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar2.add(btnFormatStandard); + + btnFormatModern.setSelected(true); + btnFormatModern.setText("Modern"); + btnFormatModern.setToolTipText("Modern format."); + btnFormatModern.setFocusPainted(false); + btnFormatModern.setFocusable(false); + btnFormatModern.setRequestFocusEnabled(false); + btnFormatModern.setVerifyInputWhenFocusTarget(false); + btnFormatModern.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar2.add(btnFormatModern); + + btnFormatLegacy.setSelected(true); + btnFormatLegacy.setText("Legacy"); + btnFormatLegacy.setToolTipText("Legacy format."); + btnFormatLegacy.setFocusPainted(false); + btnFormatLegacy.setFocusable(false); + btnFormatLegacy.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnFormatLegacy.setRequestFocusEnabled(false); + btnFormatLegacy.setVerifyInputWhenFocusTarget(false); + btnFormatLegacy.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnFormatLegacy.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar2.add(btnFormatLegacy); + + btnFormatVintage.setSelected(true); + btnFormatVintage.setText("Vintage"); + btnFormatVintage.setToolTipText("Vintage format."); + btnFormatVintage.setFocusPainted(false); + btnFormatVintage.setFocusable(false); + btnFormatVintage.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnFormatVintage.setRequestFocusEnabled(false); + btnFormatVintage.setVerifyInputWhenFocusTarget(false); + btnFormatVintage.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnFormatVintage.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar2.add(btnFormatVintage); + filterBar2.add(jSeparator3); + + btnFormatCommander.setSelected(true); + btnFormatCommander.setText("Commander"); + btnFormatCommander.setToolTipText("Commander format."); + btnFormatCommander.setFocusPainted(false); + btnFormatCommander.setFocusable(false); + btnFormatCommander.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnFormatCommander.setRequestFocusEnabled(false); + btnFormatCommander.setVerifyInputWhenFocusTarget(false); + btnFormatCommander.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnFormatCommander.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar2.add(btnFormatCommander); + + btnFormatTinyLeader.setSelected(true); + btnFormatTinyLeader.setText("Tiny Leader"); + btnFormatTinyLeader.setToolTipText("Tiny Leader format."); + btnFormatTinyLeader.setFocusPainted(false); + btnFormatTinyLeader.setFocusable(false); + btnFormatTinyLeader.setRequestFocusEnabled(false); + btnFormatTinyLeader.setVerifyInputWhenFocusTarget(false); + btnFormatTinyLeader.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar2.add(btnFormatTinyLeader); + filterBar2.add(jSeparator2); + + btnFormatLimited.setSelected(true); + btnFormatLimited.setText("Limited"); + btnFormatLimited.setToolTipText("Limited format."); + btnFormatLimited.setFocusPainted(false); + btnFormatLimited.setFocusable(false); + btnFormatLimited.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnFormatLimited.setRequestFocusEnabled(false); + btnFormatLimited.setVerifyInputWhenFocusTarget(false); + btnFormatLimited.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnFormatLimited.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar2.add(btnFormatLimited); + + btnFormatOther.setSelected(true); + btnFormatOther.setText("Other"); + btnFormatOther.setToolTipText("Other formats (Freeform, Pauper, Extended, etc.)"); + btnFormatOther.setFocusPainted(false); + btnFormatOther.setFocusable(false); + btnFormatOther.setRequestFocusEnabled(false); + btnFormatOther.setVerifyInputWhenFocusTarget(false); + btnFormatOther.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar2.add(btnFormatOther); + filterBar2.add(jSeparator5); + + btnOpen.setSelected(true); + btnOpen.setText("Open"); + btnOpen.setToolTipText("Show open games"); + btnOpen.setFocusPainted(false); + btnOpen.setFocusable(false); + btnOpen.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnOpen.setRequestFocusEnabled(false); + btnOpen.setVerifyInputWhenFocusTarget(false); + btnOpen.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnOpen.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar2.add(btnOpen); + + btnPassword.setSelected(true); + btnPassword.setText("PW"); + btnPassword.setToolTipText("Show passworded games"); + btnPassword.setFocusPainted(false); + btnPassword.setFocusable(false); + btnPassword.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnPassword.setRequestFocusEnabled(false); + btnPassword.setVerifyInputWhenFocusTarget(false); + btnPassword.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnPassword.addActionListener(evt -> btnFilterActionPerformed(evt)); + filterBar2.add(btnPassword); + + btnQuickStart.setText("Quick Start"); + btnQuickStart.setFocusable(false); + btnQuickStart.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + btnQuickStart.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + btnQuickStart.addActionListener(evt -> btnQuickStartActionPerformed(evt)); + + javax.swing.GroupLayout jPanelTopLayout = new javax.swing.GroupLayout(jPanelTop); + jPanelTop.setLayout(jPanelTopLayout); + jPanelTopLayout.setHorizontalGroup( + jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanelTopLayout.createSequentialGroup() + .addContainerGap() + .addComponent(btnNewTable) + .addGap(6, 6, 6) + .addComponent(btnNewTournament) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(filterBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 491, Short.MAX_VALUE) + .addComponent(filterBar2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnQuickStart) + .addContainerGap(835, Short.MAX_VALUE)) + ); + jPanelTopLayout.setVerticalGroup( + jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanelTopLayout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(btnNewTable) + .addComponent(btnNewTournament)) + .addGroup(jPanelTopLayout.createSequentialGroup() + .addGroup(jPanelTopLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(filterBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(btnQuickStart)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(filterBar2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addContainerGap()) + ); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + add(jPanelTop, gridBagConstraints); + + jSplitPane1.setBorder(null); + jSplitPane1.setDividerSize(10); + jSplitPane1.setResizeWeight(1.0); + + jSplitPaneTables.setBorder(null); + jSplitPaneTables.setDividerSize(10); + jSplitPaneTables.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); + jSplitPaneTables.setResizeWeight(0.5); + + jScrollPaneTablesActive.setBorder(null); + jScrollPaneTablesActive.setViewportBorder(null); + + tableTables.setModel(this.tableModel); + jScrollPaneTablesActive.setViewportView(tableTables); + + jSplitPaneTables.setLeftComponent(jScrollPaneTablesActive); + + jScrollPaneTablesFinished.setBorder(null); + jScrollPaneTablesFinished.setViewportBorder(null); + jScrollPaneTablesFinished.setMinimumSize(new java.awt.Dimension(23, 0)); + + tableCompleted.setModel(this.matchesModel); + jScrollPaneTablesFinished.setViewportView(tableCompleted); + + jSplitPaneTables.setRightComponent(jScrollPaneTablesFinished); + + javax.swing.GroupLayout jPanelTablesLayout = new javax.swing.GroupLayout(jPanelTables); + jPanelTables.setLayout(jPanelTablesLayout); + jPanelTablesLayout.setHorizontalGroup( + jPanelTablesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jSplitPaneTables, javax.swing.GroupLayout.PREFERRED_SIZE, 23, Short.MAX_VALUE) + ); + jPanelTablesLayout.setVerticalGroup( + jPanelTablesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jSplitPaneTables, javax.swing.GroupLayout.DEFAULT_SIZE, 672, Short.MAX_VALUE) + ); + + jSplitPane1.setLeftComponent(jPanelTables); + jSplitPane1.setRightComponent(chatPanelMain); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + add(jSplitPane1, gridBagConstraints); + + jPanelBottom.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); + jPanelBottom.setPreferredSize(new java.awt.Dimension(516, 37)); + jPanelBottom.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT)); + + jButtonFooterNext.setText("Next"); + jButtonFooterNext.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + jButtonFooterNext.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButtonFooterNext.setOpaque(false); + jButtonFooterNext.addActionListener(evt -> jButtonFooterNextActionPerformed(evt)); + jPanelBottom.add(jButtonFooterNext); + + jLabelFooterLabel.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N + jLabelFooterLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + jLabelFooterLabel.setText("Message of the Day:"); + jLabelFooterLabel.setAlignmentY(0.3F); + jPanelBottom.add(jLabelFooterLabel); + + jLabelFooterText.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + jLabelFooterText.setText("You are playing Mage version 0.7.5. Welcome! -- Mage dev team --"); + jPanelBottom.add(jLabelFooterText); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridy = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + add(jPanelBottom, gridBagConstraints); + }//
//GEN-END:initComponents + + private void btnNewTournamentActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnNewTournamentActionPerformed + newTournamentDialog.showDialog(roomId); + }//GEN-LAST:event_btnNewTournamentActionPerformed + + private void btnQuickStartActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuickStartActionPerformed + TableView table; + try { + File f = new File("test.dck"); + if (!f.exists()) { + JOptionPane.showMessageDialog(null, "Couldn't find test.dck file for quick game start", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + + MatchOptions options = new MatchOptions("1", "Two Player Duel", false, 2); + options.getPlayerTypes().add(PlayerType.HUMAN); + options.getPlayerTypes().add(PlayerType.COMPUTER_MAD); + options.setDeckType("Limited"); + options.setAttackOption(MultiplayerAttackOption.LEFT); + options.setRange(RangeOfInfluence.ALL); + options.setWinsNeeded(1); + options.setMatchTimeLimit(MatchTimeLimit.NONE); + options.setFreeMulligans(2); + options.setSkillLevel(SkillLevel.CASUAL); + options.setRollbackTurnsAllowed(true); + options.setQuitRatio(100); + String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> ""); + options.setBannedUsers(IgnoreList.ignoreList(serverAddress)); + table = SessionHandler.createTable(roomId, options); + + SessionHandler.joinTable(roomId, table.getTableId(), "Human", PlayerType.HUMAN, 1, DeckImporterUtil.importDeck("test.dck"), ""); + SessionHandler.joinTable(roomId, table.getTableId(), "Computer", PlayerType.COMPUTER_MAD, 5, DeckImporterUtil.importDeck("test.dck"), ""); + SessionHandler.startMatch(roomId, table.getTableId()); + } catch (HeadlessException ex) { + handleError(ex); + } + }//GEN-LAST:event_btnQuickStartActionPerformed + + private void btnNewTableActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnNewTableActionPerformed + newTableDialog.showDialog(roomId); + }//GEN-LAST:event_btnNewTableActionPerformed + + private void jButtonFooterNextActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonFooterNextActionPerformed + synchronized (this) { + if (messages != null && !messages.isEmpty()) { + currentMessage++; + if (currentMessage >= messages.size()) { + currentMessage = 0; + } + + URLHandler.RemoveMouseAdapter(jLabelFooterText); + URLHandler.handleMessage(messages.get(currentMessage), this.jLabelFooterText); + } + } + }//GEN-LAST:event_jButtonFooterNextActionPerformed + + private void btnFilterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFilterActionPerformed + setTableFilter(); + }//GEN-LAST:event_btnFilterActionPerformed + + private void btnStateFinishedActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnStateFinishedActionPerformed + if (this.btnStateFinished.isSelected()) { + this.jSplitPaneTables.setDividerLocation(-1); + } else { + this.jSplitPaneTables.setDividerLocation(this.jPanelTables.getHeight()); + } + this.startTasks(); + }//GEN-LAST:event_btnStateFinishedActionPerformed + + private void handleError(Exception ex) { + LOGGER.fatal("Error loading deck: ", ex); + JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Error loading deck.", "Error", JOptionPane.ERROR_MESSAGE); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JToggleButton btnFormatBlock; + private javax.swing.JToggleButton btnFormatCommander; + private javax.swing.JToggleButton btnFormatLegacy; + private javax.swing.JToggleButton btnFormatLimited; + private javax.swing.JToggleButton btnFormatModern; + private javax.swing.JToggleButton btnFormatOther; + private javax.swing.JToggleButton btnFormatStandard; + private javax.swing.JToggleButton btnFormatTinyLeader; + private javax.swing.JToggleButton btnFormatVintage; + private javax.swing.JButton btnNewTable; + private javax.swing.JButton btnNewTournament; + private javax.swing.JToggleButton btnOpen; + private javax.swing.JToggleButton btnPassword; + private javax.swing.JButton btnQuickStart; + private javax.swing.JToggleButton btnSkillBeginner; + private javax.swing.JToggleButton btnSkillCasual; + private javax.swing.JToggleButton btnSkillSerious; + private javax.swing.JToggleButton btnRated; + private javax.swing.JToggleButton btnUnrated; + private javax.swing.JToggleButton btnStateActive; + private javax.swing.JToggleButton btnStateFinished; + private javax.swing.JToggleButton btnStateWaiting; + private javax.swing.JToggleButton btnTypeMatch; + private javax.swing.JToggleButton btnTypeTourneyConstructed; + private javax.swing.JToggleButton btnTypeTourneyLimited; + private mage.client.table.PlayersChatPanel chatPanelMain; + private javax.swing.JToolBar filterBar1; + private javax.swing.JToolBar filterBar2; + private javax.swing.JButton jButtonFooterNext; + private javax.swing.JLabel jLabelFooterLabel; + private javax.swing.JLabel jLabelFooterText; + private javax.swing.JPanel jPanelBottom; + private javax.swing.JPanel jPanelTables; + private javax.swing.JPanel jPanelTop; + private javax.swing.JScrollPane jScrollPaneTablesActive; + private javax.swing.JScrollPane jScrollPaneTablesFinished; + private javax.swing.JToolBar.Separator jSeparator1; + private javax.swing.JToolBar.Separator jSeparator2; + private javax.swing.JToolBar.Separator jSeparator3; + private javax.swing.JToolBar.Separator jSeparator4; + private javax.swing.JToolBar.Separator jSeparator5; + private javax.swing.JSplitPane jSplitPane1; + private javax.swing.JSplitPane jSplitPaneTables; + private javax.swing.JTable tableCompleted; + private javax.swing.JTable tableTables; + // End of variables declaration//GEN-END:variables + + } + + class TableTableModel extends AbstractTableModel { + + final ImageIcon tourneyIcon = new javax.swing.ImageIcon(getClass().getResource("/tables/tourney_icon.png")); + final ImageIcon matchIcon = new javax.swing.ImageIcon(getClass().getResource("/tables/match_icon.png")); + + public static final int COLUMN_ICON = 0; + public static final int COLUMN_DECK_TYPE = 1; // column the deck type is located (starting with 0) Start string is used to check for Limited + public static final int COLUMN_OWNER = 2; + public static final int COLUMN_GAME_TYPE = 3; + public static final int COLUMN_INFO = 4; + public static final int COLUMN_STATUS = 5; + public static final int COLUMN_PASSWORD = 6; + public static final int COLUMN_CREATED = 7; + public static final int COLUMN_SKILL = 8; + public static final int COLUMN_RATING = 9; + public static final int COLUMN_QUIT_RATIO = 10; + public static final int ACTION_COLUMN = 11; // column the action is located (starting with 0) + + public static final String RATED_VALUE_YES = "YES"; + public static final String RATED_VALUE_NO = ""; + + public static final String PASSWORD_VALUE_YES = "YES"; + + private final String[] columnNames = new String[]{"M/T", "Deck Type", "Owner / Players", "Game Type", "Info", "Status", "Password", "Created / Started", "Skill Level", "Rating", "Quit %", "Action"}; + + private TableView[] tables = new TableView[0]; + + TableTableModel() { + } + + public void loadData(Collection tables) throws MageRemoteException { + this.tables = tables.toArray(new TableView[0]); + this.fireTableDataChanged(); + } + + public String getSkillLevelAsCode(SkillLevel skill, boolean asRegExp) { + String res; + switch (skill) { + case BEGINNER: + res = "*"; + break; + case CASUAL: + res = "**"; + break; + case SERIOUS: + res = "***"; + break; + default: + res = ""; + break; + } + + // regexp format for search table rows + if (asRegExp) { + res = String.format("^%s$", res.replace("*", "\\*")); + } + + return res; + } + + @Override + public int getRowCount() { + return tables.length; + } + + @Override + public int getColumnCount() { + return columnNames.length; + } + + @Override + public Object getValueAt(int arg0, int arg1) { + switch (arg1) { + case 0: + return tables[arg0].isTournament() ? tourneyIcon : matchIcon; + case 1: + return tables[arg0].getDeckType(); + case 2: + return tables[arg0].getControllerName(); + case 3: + return tables[arg0].getGameType(); + case 4: + return tables[arg0].getAdditionalInfo(); + case 5: + return tables[arg0].getTableStateText(); + case 6: + return tables[arg0].isPassworded() ? PASSWORD_VALUE_YES : ""; + case 7: + return tables[arg0].getCreateTime(); // use cell render, not format here + case 8: + return this.getSkillLevelAsCode(tables[arg0].getSkillLevel(), false); + case 9: + return tables[arg0].isRated() ? RATED_VALUE_YES : RATED_VALUE_NO; + case 10: + return tables[arg0].getQuitRatio(); + case 11: + switch (tables[arg0].getTableState()) { + + case WAITING: + String owner = tables[arg0].getControllerName(); + if (SessionHandler.getSession() != null && owner.equals(SessionHandler.getUserName())) { + return ""; + } + return "Join"; + case CONSTRUCTING: + case DRAFTING: + if (tables[arg0].isTournament()) { + return "Show"; + } + case DUELING: + if (tables[arg0].isTournament()) { + return "Show"; + } else { + owner = tables[arg0].getControllerName(); + if (SessionHandler.getSession() != null && owner.equals(SessionHandler.getUserName())) { + return ""; + } + if (tables[arg0].getSpectatorsAllowed()) { + return "Watch"; + } + return ""; + } + default: + return ""; + } + case 12: + return tables[arg0].isTournament(); + case 13: + if (!tables[arg0].getGames().isEmpty()) { + return tables[arg0].getGames().get(0); + } + return null; + case 14: + return tables[arg0].getTableId(); + } + return ""; + } + + @Override + public String getColumnName(int columnIndex) { + String colName = ""; + + if (columnIndex <= getColumnCount()) { + colName = columnNames[columnIndex]; + } + + return colName; + } + + @Override + public Class getColumnClass(int columnIndex) { + switch (columnIndex) { + case COLUMN_ICON: + return Icon.class; + case COLUMN_SKILL: + return SkillLevel.class; + case COLUMN_CREATED: + return Date.class; + default: + return String.class; + } + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return columnIndex == ACTION_COLUMN; + } + + } + + class UpdateTablesTask extends SwingWorker> { + + private final UUID roomId; + private final TablesPanel panel; + + private static final Logger logger = Logger.getLogger(UpdateTablesTask.class); + + private int count = 0; + + UpdateTablesTask(UUID roomId, TablesPanel panel) { + + this.roomId = roomId; + this.panel = panel; + } + + @Override + protected Void doInBackground() throws Exception { + while (!isCancelled()) { + Collection tables = SessionHandler.getTables(roomId); + if (tables != null) { + this.publish(tables); + } + TimeUnit.SECONDS.sleep(3); + } + return null; + } + + @Override + protected void process(java.util.List> view) { + panel.updateTables(view.get(0)); + count++; + if (count > 60) { + count = 0; + panel.reloadMessages(); + } + } + + @Override + protected void done() { + try { + get(); + } catch (InterruptedException | ExecutionException ex) { + logger.fatal("Update Tables Task error", ex); + } catch (CancellationException ex) { + } + } + + } + + class UpdatePlayersTask extends SwingWorker> { + + private final UUID roomId; + private final PlayersChatPanel chat; + + private static final Logger logger = Logger.getLogger(UpdatePlayersTask.class); + + UpdatePlayersTask(UUID roomId, PlayersChatPanel chat) { + + this.roomId = roomId; + this.chat = chat; + } + + @Override + protected Void doInBackground() throws Exception { + while (!isCancelled()) { + this.publish(SessionHandler.getRoomUsers(roomId)); + TimeUnit.SECONDS.sleep(3); + } + return null; + } + + @Override + protected void process(java.util.List> roomUserInfo) { + chat.setRoomUserInfo(roomUserInfo); + } + + @Override + protected void done() { + try { + get(); + } catch (InterruptedException | ExecutionException ex) { + logger.fatal("Update Players Task error", ex); + } catch (CancellationException ex) { + } + } + + } + + class MatchesTableModel extends AbstractTableModel { + + private final String[] columnNames = new String[]{"Deck Type", "Players", "Game Type", "Rating", "Result", "Duration", "Start Time", "End Time", "Action"}; + public static final int COLUMN_DURATION = 5; + public static final int COLUMN_START = 6; + public static final int COLUMN_END = 7; + public static final int COLUMN_ACTION = 8; // column the action is located (starting with 0) + + private MatchView[] matches = new MatchView[0]; + + public void loadData(Collection matches) throws MageRemoteException { + this.matches = matches.toArray(new MatchView[0]); + this.fireTableDataChanged(); + } + + MatchesTableModel() { + } + + @Override + public int getRowCount() { + return matches.length; + } + + @Override + public int getColumnCount() { + return columnNames.length; + } + + @Override + public Object getValueAt(int arg0, int arg1) { + switch (arg1) { + case 0: + return matches[arg0].getDeckType(); + case 1: + return matches[arg0].getPlayers(); + case 2: + return matches[arg0].getGameType(); + case 3: + return matches[arg0].isRated() ? TableTableModel.RATED_VALUE_YES : TableTableModel.RATED_VALUE_NO; + case 4: + return matches[arg0].getResult(); + case 5: + if (matches[arg0].getEndTime() != null) { + return matches[arg0].getEndTime().getTime() - matches[arg0].getStartTime().getTime() + new Date().getTime(); + } else { + return 0L; + } + case 6: + return matches[arg0].getStartTime(); + case 7: + return matches[arg0].getEndTime(); + case 8: + if (matches[arg0].isTournament()) { + return "Show"; + } else if (matches[arg0].isReplayAvailable()) { + return "Replay"; + } else { + return "None"; + } + case 9: + return matches[arg0].getGames(); + } + return ""; + } + + public java.util.List getListofGames(int row) { + return matches[row].getGames(); + } + + public boolean isTournament(int row) { + return matches[row].isTournament(); + } + + public UUID getMatchId(int row) { + return matches[row].getMatchId(); + } + + public UUID getTableId(int row) { + return matches[row].getTableId(); + } + + @Override + public String getColumnName(int columnIndex) { + String colName = ""; + + if (columnIndex <= getColumnCount()) { + colName = columnNames[columnIndex]; + } + + return colName; + } + + @Override + public Class getColumnClass(int columnIndex) { + switch (columnIndex) { + case COLUMN_DURATION: + return Long.class; + case COLUMN_START: + return Date.class; + case COLUMN_END: + return Date.class; + default: + return String.class; + } + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return columnIndex == COLUMN_ACTION; + } + + } + + class UpdateMatchesTask extends SwingWorker> { + + private final UUID roomId; + private final TablesPanel panel; + + private static final Logger logger = Logger.getLogger(UpdateTablesTask.class); + + UpdateMatchesTask(UUID roomId, TablesPanel panel) { + this.roomId = roomId; + this.panel = panel; + } + + @Override + protected Void doInBackground() throws Exception { + while (!isCancelled()) { + Collection matches = SessionHandler.getFinishedMatches(roomId); + if (!matches.isEmpty()) { + this.publish(matches); + } + TimeUnit.SECONDS.sleep(10); + } + return null; + } + + @Override + protected void process(java.util.List> view) { + panel.updateMatches(view.get(0)); + } + + @Override + protected void done() { + try { + get(); + } catch (InterruptedException | ExecutionException ex) { + logger.fatal("Update Matches Task error", ex); + } catch (CancellationException ex) { + } + } + + } + + class GameChooser extends JPopupMenu { + + public void init() { + + } + + public void show(java.util.List games, Point p) { + if (p == null) { + return; + } + this.removeAll(); + for (UUID gameId : games) { + this.add(new GameChooserAction(gameId, gameId.toString())); + } + this.show(MageFrame.getDesktop(), p.x, p.y); + GuiDisplayUtil.keepComponentInsideScreen(p.x, p.y, this); + } + + private class GameChooserAction extends AbstractAction { + + private final UUID id; + + public GameChooserAction(UUID id, String choice) { + this.id = id; + putValue(Action.NAME, choice); + } + + @Override + public void actionPerformed(ActionEvent e) { + SessionHandler.replayGame(id); + setVisible(false); + } + + } + + } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index 3702f176cb2..f32c08e972d 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.card.arcane; import java.awt.*; @@ -63,26 +58,26 @@ public final class CardRendererUtils { int plus_b = (int) ((255 - b) / 2); return new Color(r + plus_r, - g + plus_g, - b + plus_b, - alpha); + g + plus_g, + b + plus_b, + alpha); } - + public static Color abitdarker(Color c) { int r = c.getRed(); int g = c.getGreen(); int b = c.getBlue(); int alpha = c.getAlpha(); - int plus_r = (int) (Math.min (255 - r, r) / 2); - int plus_g = (int) (Math.min (255 - g, g) / 2); - int plus_b = (int) (Math.min (255 - b, b) / 2); + int plus_r = (int) (Math.min(255 - r, r) / 2); + int plus_g = (int) (Math.min(255 - g, g) / 2); + int plus_b = (int) (Math.min(255 - b, b) / 2); return new Color(r - plus_r, - g - plus_g, - b - plus_b, - alpha); - } + g - plus_g, + b - plus_b, + alpha); + } // Draw a rounded box with a 2-pixel border // Used on various card parts. @@ -192,4 +187,12 @@ public final class CardRendererUtils { .replaceAll("", "") .replaceAll("", ""); } + + public static Color copyColor(Color color) { + if (color != null) { + return new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); + } else { + return null; + } + } } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbolsCellRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbolsCellRenderer.java index 46a0e3f1c2c..bd9731ec6a4 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbolsCellRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbolsCellRenderer.java @@ -9,10 +9,13 @@ import java.awt.*; import java.awt.image.BufferedImage; import java.util.StringTokenizer; +/** + * @author JayDi85 + */ public final class ManaSymbolsCellRenderer extends DefaultTableCellRenderer { // base panel to render - private JPanel manaPanel = new JPanel(); + private JPanel renderPanel = new JPanel(); @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, @@ -20,47 +23,47 @@ public final class ManaSymbolsCellRenderer extends DefaultTableCellRenderer { // get table text cell settings DefaultTableCellRenderer baseRenderer = (DefaultTableCellRenderer) table.getDefaultRenderer(String.class); - JLabel baseLabel = (JLabel)baseRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + JLabel baseComp = (JLabel) baseRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); // apply settings to mana panel from parent - manaPanel.setOpaque(baseLabel.isOpaque()); - manaPanel.setForeground(baseLabel.getForeground()); - manaPanel.setBackground(baseLabel.getBackground()); + renderPanel.setOpaque(baseComp.isOpaque()); + renderPanel.setForeground(CardRendererUtils.copyColor(baseComp.getForeground())); + renderPanel.setBackground(CardRendererUtils.copyColor(baseComp.getBackground())); + renderPanel.setBorder(baseComp.getBorder()); // icons size with margin int symbolWidth = GUISizeHelper.symbolTableSize; int symbolHorizontalMargin = 2; // create each mana symbol as child label - String manaCost = (String)value; - manaPanel.removeAll(); - manaPanel.setLayout(new BoxLayout(manaPanel, BoxLayout.X_AXIS)); - if(manaCost != null){ + String manaCost = (String) value; + renderPanel.removeAll(); + renderPanel.setLayout(new BoxLayout(renderPanel, BoxLayout.X_AXIS)); + if (manaCost != null) { StringTokenizer tok = new StringTokenizer(manaCost, " "); while (tok.hasMoreTokens()) { String symbol = tok.nextToken(); JLabel symbolLabel = new JLabel(); //symbolLabel.setBorder(new LineBorder(new Color(150, 150, 150))); // debug - symbolLabel.setBorder(new EmptyBorder(0, symbolHorizontalMargin,0, 0)); + symbolLabel.setBorder(new EmptyBorder(0, symbolHorizontalMargin, 0, 0)); BufferedImage image = ManaSymbols.getSizedManaSymbol(symbol, symbolWidth); - if (image != null){ + if (image != null) { // icon symbolLabel.setIcon(new ImageIcon(image)); - }else - { + } else { // text symbolLabel.setText("{" + symbol + "}"); - symbolLabel.setOpaque(baseLabel.isOpaque()); - symbolLabel.setForeground(baseLabel.getForeground()); - symbolLabel.setBackground(baseLabel.getBackground()); + symbolLabel.setOpaque(baseComp.isOpaque()); + symbolLabel.setForeground(baseComp.getForeground()); + symbolLabel.setBackground(baseComp.getBackground()); } - manaPanel.add(symbolLabel); + renderPanel.add(symbolLabel); } } - return manaPanel; + return renderPanel; } } diff --git a/Mage.Client/src/main/resources/info/yellow_star_16.png b/Mage.Client/src/main/resources/info/yellow_star_16.png new file mode 100644 index 0000000000000000000000000000000000000000..849916980af0a76d8717cc5bc814aa49ee50db28 GIT binary patch literal 478 zcmV<40U`d0P)5kugscQ51#0b7yuKSVLhU1tt=AV&h^shc(^*sz-|q|8+LwhY7rIWh;VXLqMcPuS+p6={~M^*>&jH7?1lob5NOE5w2UDf^gTk` z!9o2GP~v2Zh9y`!r{=peZ4bRK-v(&45B==1B0zY}qNc|Ar8GC!w_+Hm%eZN!E6ZlB@3PskauiVq9`}3mk#5&_{^l$_@tM=lc2u4M?rnCg4D15@2Ijws UXwz-TT>t<807*qoM6N<$f}&T~`~Uy| literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/info/yellow_star_24.png b/Mage.Client/src/main/resources/info/yellow_star_24.png new file mode 100644 index 0000000000000000000000000000000000000000..41d21f49db2762b8fd01fe75600c69a438dc97f9 GIT binary patch literal 658 zcmV;D0&V??P)0B4t`CbMJ9sXxh#|ncB0O zIrn|v=YGswAV&EMsTF@AwGzi84l1m^&s<|iXV}phzc_HU>i}h{ySvY91yfxG_{Gcx zIJ&RiaCG11yMc3E1He))w*S8YY^%pj4GoAj8?@R( zT;m(<{M6d0%pW^K#?D!-DF=&l?5Y!W@wNc{v;kGODG(kJRUk4XbT(MEw%*>DGR; zqRo!Ow}s$KqIcxSJt|w1kq}?}+m`tCM>8%a^2=`p03=7gO_Og-H=TJv5NmcP;X4e< z_m0UoK*ML6Jo9}Lzs3FCcPiO{-uGytDq!NVq__0Cp3|z>`jC1^T3n}fD{i_1aM|_b zv&Lp*NTrZ{0*&#QfTyk}ue3I!|76VvzGdr0(UXl1Vj7AKU_^RC0G@*Z!eDFwFvxQ- z+5&3gcp5-Y2Y^Hk!=c+X1W>H^+Ma-bSpsGO1W^KoAmj}J1S0mIQ4l1C98XR-o}3UQ zh5&vM_HxK;hO-()^bvw@x_PHq)$ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/info/yellow_star_32.png b/Mage.Client/src/main/resources/info/yellow_star_32.png new file mode 100644 index 0000000000000000000000000000000000000000..029025a9a9ea63b71dbb9ce90827471f3b8109f8 GIT binary patch literal 931 zcmV;U16=%xP)noJ zwGv5qwqG6Tr$}o!(Sw8ks+~9h(vqgPw6sSiY4)ns*A9etkDS>s2T)s`aR12ZezbZ9 zP{mMez0x@@ZtGRm-t~Ge2)`YE9Y?(_V2+j>-wjReIsi^Goi(M7zPYOa!TG`C6qCC$ z0anev=*xpgb`3y^q&M1{L9V!%zYCDqZ{!Z3iV4mSf276a-Yrl!Y|Ni7va%ti&quY< z7gV=*#M_ee>%oCwuX$E1dD4hJqLK__>wXIAsdNY?Aj>Y&NMD1(Em3>~UzB+TKSBI* zxbmAEUtGBZ5Y7&NhNI(Z#{<~d+oEn7xK>zy_8y9`Bndu-FC@J1K(b|Z-D1UHmK})u zx$R1uWN~BYISMC|2clbq6jBMIDxR(Emc)|+kx9BAgkyms#7H|u9hbJbEOIY zT*-^8s7?VlGit`NPD1KnP#Zg7q;<7pxC#16v+2G!@bWLIM*q~t_7zP07OE!!XY*XL zRya_3X>Pf*CUplat2x@iIU`EdJGO8t%N$_u%faqZS+Hs_Yp=kmdk0m{{iKaHJz?eVVD2_002ovPDHLk FV1gZQw15Br literal 0 HcmV?d00001 From b1df464e8fa2777b3bfb956e1e3f88a5bf92ea37 Mon Sep 17 00:00:00 2001 From: John Hitchings Date: Fri, 16 Nov 2018 21:57:25 -0800 Subject: [PATCH 17/61] refactor deck import content to include hints --- .../DeckImportFromClipboardDialog.java | 20 ++++++++++++++++--- .../mage/client/plugins/impl/Plugins.java | 10 +++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportFromClipboardDialog.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportFromClipboardDialog.java index 6c890632173..4b370eee774 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportFromClipboardDialog.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportFromClipboardDialog.java @@ -16,6 +16,15 @@ import javax.swing.*; public class DeckImportFromClipboardDialog extends JDialog { + private static final String FORMAT_TEXT = + "// Example:\n" + + "//1 Library of Congress\n" + + "//1 Cryptic Gateway\n" + + "//1 Azami, Lady of Scrolls\n" + + "// NB: This is slow as, and will lock your screen :)\n" + + "\n" + + "// Your current clipboard:\n"; + private JPanel contentPane; private JButton buttonOK; private JButton buttonCancel; @@ -25,6 +34,9 @@ public class DeckImportFromClipboardDialog extends JDialog { public DeckImportFromClipboardDialog() { initComponents(); + + onRefreshClipboard(); + setContentPane(contentPane); setModal(true); getRootPane().setDefaultButton(buttonOK); @@ -42,8 +54,6 @@ public class DeckImportFromClipboardDialog extends JDialog { // Close on "ESC" contentPane.registerKeyboardAction(e -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - - getClipboardStringData().ifPresent(content -> txtDeckList.setText(content)); } private Optional getClipboardStringData() { @@ -75,6 +85,10 @@ public class DeckImportFromClipboardDialog extends JDialog { dispose(); } + private void onRefreshClipboard() { + txtDeckList.setText(FORMAT_TEXT + getClipboardStringData().orElse("")); + } + public String getTmpPath() { return tmpPath; } @@ -158,7 +172,7 @@ public class DeckImportFromClipboardDialog extends JDialog { txtDeckList.setMinimumSize(new Dimension(250, 400)); txtDeckList.setPreferredSize(new Dimension(550, 400)); - txtDeckList.setText("// Example:\n//1 Library of Congress\n//1 Cryptic Gateway\n//1 Azami, Lady of Scrolls\n// NB: This is slow as, and will lock your screen :)"); + txtDeckList.setText(FORMAT_TEXT); JScrollPane txtScrollableDeckList = new JScrollPane(txtDeckList); panel3.add(txtScrollableDeckList, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, diff --git a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java index d9f1fef14ff..1731b0b656d 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java +++ b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java @@ -26,6 +26,8 @@ import mage.view.CardView; import mage.view.PermanentView; import net.xeoh.plugins.base.PluginManager; import net.xeoh.plugins.base.impl.PluginManagerFactory; +import net.xeoh.plugins.base.util.uri.ClassURI; + import org.apache.log4j.Logger; import org.mage.plugins.card.CardPluginImpl; import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir; @@ -46,13 +48,15 @@ public enum Plugins implements MagePlugins { @Override public void loadPlugins() { - LOGGER.info("Loading plugins..."); pm = PluginManagerFactory.createPluginManager(); pm.addPluginsFrom(new File(PLUGINS_DIRECTORY + File.separator).toURI()); - this.cardPlugin = new CardPluginImpl(); + pm.addPluginsFrom(new ClassURI(CardPluginImpl.class).toURI()); + pm.addPluginsFrom(new ClassURI(ThemePluginImpl.class).toURI()); + + this.cardPlugin = pm.getPlugin(CardPlugin.class); this.counterPlugin = pm.getPlugin(CounterPlugin.class); - this.themePlugin = new ThemePluginImpl(); + this.themePlugin = pm.getPlugin(ThemePlugin.class); LOGGER.info("Done."); } From 30c373c40b18fc596fc8a244e205cf73366a3070 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 17 Nov 2018 11:43:24 +0400 Subject: [PATCH 18/61] Removed bloated log on non text clipboard data --- .../mage/client/deckeditor/DeckImportFromClipboardDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportFromClipboardDialog.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportFromClipboardDialog.java index 4b370eee774..32f63c8d187 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportFromClipboardDialog.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckImportFromClipboardDialog.java @@ -60,7 +60,7 @@ public class DeckImportFromClipboardDialog extends JDialog { try { return Optional.of((String)Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor)); } catch (HeadlessException | UnsupportedFlavorException | IOException e) { - e.printStackTrace(); + //e.printStackTrace(); } return Optional.empty(); } From 5ffb6e1fd7bf18dad994f34bfa0635560401aa80 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sun, 18 Nov 2018 04:15:47 +0400 Subject: [PATCH 19/61] * Changed default US server from vaporservermtg.com to mtg.powersofwar.com --- .../src/main/java/mage/client/dialog/ConnectDialog.form | 4 ++-- .../src/main/java/mage/client/dialog/ConnectDialog.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form index 5b74e76cbf5..c1ef452cc22 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form @@ -340,8 +340,8 @@ - - + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java index 120c35e2c03..10e188e3336 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java @@ -270,8 +270,8 @@ public class ConnectDialog extends MageDialog { }); btnFind3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/flags/us.png"))); // NOI18N - btnFind3.setText("W"); - btnFind3.setToolTipText("Connect to vaporservermtg.com (USA)"); + btnFind3.setText("P"); + btnFind3.setToolTipText("Connect to mtg.powersofwar.com (USA)"); btnFind3.setActionCommand("connectXmageus"); btnFind3.setAlignmentY(0.0F); btnFind3.setMargin(new java.awt.Insets(2, 2, 2, 2)); @@ -688,7 +688,7 @@ public class ConnectDialog extends MageDialog { }//GEN-LAST:event_btnFind2findPublicServerActionPerformed private void connectXmageus(java.awt.event.ActionEvent evt) { - String serverAddress = "vapormtgserver.com"; + String serverAddress = "mtg.powersofwar.com"; this.txtServer.setText(serverAddress); this.txtPort.setText("17171"); // Update userName and password according to the chosen server. From d78d818a667d383058ef5673fa1f4bc334f58d01 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 19 Nov 2018 01:03:05 +0400 Subject: [PATCH 20/61] * Fixed not working deck generation for some settings (#5413); --- .../client/deck/generator/DeckGenerator.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java index 88c94f17118..ab7e7b07743 100644 --- a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java +++ b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java @@ -1,4 +1,3 @@ - package mage.client.deck.generator; import java.util.ArrayList; @@ -7,6 +6,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; + import mage.cards.Card; import mage.cards.decks.Deck; import mage.cards.repository.CardCriteria; @@ -123,9 +123,9 @@ public final class DeckGenerator { * non-creatures, lands (including non-basic). Fixes the deck, adjusting for * size and color of the cards retrieved. * - * @param deckSize how big the deck is to generate. + * @param deckSize how big the deck is to generate. * @param allowedColors which colors are allowed in the deck. - * @param setsToUse which sets to use to retrieve cards for this deck. + * @param setsToUse which sets to use to retrieve cards for this deck. * @return the final deck to use. */ private static Deck generateDeck(int deckSize, List allowedColors, List setsToUse) { @@ -180,9 +180,9 @@ public final class DeckGenerator { * non-creatures are retrieved separately to ensure the deck contains a * reasonable mix of both. * - * @param criteria the criteria to search for in the database. + * @param criteria the criteria to search for in the database. * @param spellCount the number of spells that match the criteria needed in - * the deck. + * the deck. */ private static void generateSpells(CardCriteria criteria, int spellCount) { List cardPool = CardRepository.instance.findCards(criteria); @@ -233,7 +233,7 @@ public final class DeckGenerator { * in this deck. Usually the lands will be well balanced relative to the * color of cards. * - * @param criteria the criteria of the lands to search for in the database. + * @param criteria the criteria of the lands to search for in the database. * @param landsCount the amount of lands required for this deck. * @param basicLands information about the basic lands from the sets used. */ @@ -310,10 +310,10 @@ public final class DeckGenerator { * filled. * * @param landsNeeded how many remaining lands are needed. - * @param percentage the percentage needed for each color in the final deck. - * @param count how many of each color can be produced by non-basic lands. - * @param basicLands list of information about basic lands from the - * database. + * @param percentage the percentage needed for each color in the final deck. + * @param count how many of each color can be produced by non-basic lands. + * @param basicLands list of information about basic lands from the + * database. */ private static void addBasicLands(int landsNeeded, Map percentage, Map count, Map> basicLands) { @@ -360,15 +360,14 @@ public final class DeckGenerator { /** * Return a random basic land of the chosen color. * - * @param color the color the basic land should produce. + * @param color the color the basic land should produce. * @param basicLands list of information about basic lands from the - * database. + * database. * @return a single basic land that produces the color needed. */ private static Card getBasicLand(ColoredManaSymbol color, Map> basicLands) { String landName = DeckGeneratorPool.getBasicLandName(color.toString()); List basicLandsInfo = basicLands.get(landName); - return basicLandsInfo.get(RandomUtil.nextInt(basicLandsInfo.size() - 1)).getMockCard().copy(); + return basicLandsInfo.get(RandomUtil.nextInt(basicLandsInfo.size())).getMockCard().copy(); } - } From 9cb69fbc836a3e7501be9a05c35ffb74504c9c2c Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 19 Nov 2018 01:46:44 +0400 Subject: [PATCH 21/61] * Mind Bomb - Fixed that it can't do damage to players with 0 selected cards; --- Mage.Sets/src/mage/cards/m/MindBomb.java | 37 +++++++----------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MindBomb.java b/Mage.Sets/src/mage/cards/m/MindBomb.java index 56a35ad408a..26b08552837 100644 --- a/Mage.Sets/src/mage/cards/m/MindBomb.java +++ b/Mage.Sets/src/mage/cards/m/MindBomb.java @@ -1,17 +1,10 @@ package mage.cards.m; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterCard; @@ -20,8 +13,11 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetDiscard; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class MindBomb extends CardImpl { @@ -67,6 +63,7 @@ class MindBombEffect extends OneShotEffect { if (controller != null && sourceObject != null) { Map cardsToDiscard = new HashMap<>(); + // choose for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { @@ -77,6 +74,8 @@ class MindBombEffect extends OneShotEffect { cardsToDiscard.put(playerId, cards); } } + + // discard for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { @@ -91,31 +90,17 @@ class MindBombEffect extends OneShotEffect { } } } + + // damage for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); if (player != null) { Cards cardsPlayer = cardsToDiscard.get(playerId); - if (cardsPlayer != null && !cardsPlayer.isEmpty()) { + if (cardsPlayer != null) { player.damage(3 - cardsPlayer.size(), source.getId(), game, false, true); } } } - // reveal the searched lands, put in hands, and shuffle -// for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { -// Player player = game.getPlayer(playerId); -// if (player != null) { -// Cards cardsPlayer = cardsToReveal.get(playerId); -// if (cardsPlayer != null) { -// for (UUID cardId : cardsPlayer) { -// Cards cards = new CardsImpl(game.getCard(cardId)); -// Card card = game.getCard(cardId); -// player.revealCards(sourceObject.getIdName() + " (" + player.getName() + ')', cards, game); -// player.moveCards(card, Zone.HAND, source, game); -// player.shuffleLibrary(source, game); -// } -// } -// } -// } return true; } return false; From f15d8a0da8d4d07839b626d09fda640d2cd80667 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 19 Nov 2018 15:39:05 +0400 Subject: [PATCH 22/61] Code cleanup; --- Mage.Client/serverlist.txt | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 Mage.Client/serverlist.txt diff --git a/Mage.Client/serverlist.txt b/Mage.Client/serverlist.txt deleted file mode 100644 index caecdb8ac02..00000000000 --- a/Mage.Client/serverlist.txt +++ /dev/null @@ -1,6 +0,0 @@ -XMage.de 1 (Europe/Germany) fast :xmage.de:17171 -old xmage.de (Europe/Germany) :185.3.232.200:17171 -XMage Players MTG:xmageplayersmtg.ddns.net:17171 -XMage.tahiti :xmage.tahiti.one:443 -Seedds Server (Asia) :115.29.203.80:17171 -localhost -> connect to your local server (must be started):localhost:17171 From d1c1abb967a19e37876d252880564b9323a9bfd0 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 19 Nov 2018 11:17:14 -0600 Subject: [PATCH 23/61] - Fixed #5433 --- .../src/mage/cards/m/MnemonicBetrayal.java | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java b/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java index 019527dd74f..ab23ae3bd35 100644 --- a/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java +++ b/Mage.Sets/src/mage/cards/m/MnemonicBetrayal.java @@ -10,10 +10,11 @@ import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; - import java.util.HashMap; import java.util.Map; import java.util.UUID; +import mage.abilities.effects.AsThoughManaEffect; +import mage.players.ManaPoolItem; /** * @author TheElk801 @@ -69,17 +70,18 @@ class MnemonicBetrayalExileEffect extends OneShotEffect { } Cards cards = new CardsImpl(); Map cardMap = new HashMap(); - for (UUID playerId : game.getOpponents(source.getControllerId())) { - Player player = game.getPlayer(playerId); - if (player != null) { - cards.addAll(player.getGraveyard()); - } - } - for (Card card : cards.getCards(game)) { + game.getOpponents(source.getControllerId()).stream().map((playerId) -> game.getPlayer(playerId)).filter((player) -> (player != null)).forEachOrdered((player) -> { + cards.addAll(player.getGraveyard()); + }); + cards.getCards(game).stream().map((card) -> { cardMap.put(card.getId(), card.getZoneChangeCounter(game)); + return card; + }).map((card) -> { game.addEffect(new MnemonicBetrayalCastFromExileEffect(card, game), source); + return card; + }).forEachOrdered((card) -> { game.addEffect(new MnemonicBetrayalAnyColorEffect(card, game), source); - } + }); controller.moveCardsToExile(cards.getCards(game), source, game, true, source.getSourceId(), source.getSourceObjectIfItStillExists(game).getName()); game.addDelayedTriggeredAbility(new MnemonicBetrayalDelayedTriggeredAbility(cards, cardMap), source); return true; @@ -125,7 +127,7 @@ class MnemonicBetrayalCastFromExileEffect extends AsThoughEffectImpl { } } -class MnemonicBetrayalAnyColorEffect extends AsThoughEffectImpl { +class MnemonicBetrayalAnyColorEffect extends AsThoughEffectImpl implements AsThoughManaEffect { private final Card card; private final int zoneCounter; @@ -154,13 +156,21 @@ class MnemonicBetrayalAnyColorEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (card.getZoneChangeCounter(game) != zoneCounter) { - this.discard(); - return false; + if (objectId.equals(card.getId()) + && card.getZoneChangeCounter(game) <= zoneCounter + 1 + && affectedControllerId.equals(source.getControllerId())) { + return true; + } else { + if (objectId.equals(card.getId())) { + this.discard(); + } } - return objectId.equals(card.getId()) - && card.getZoneChangeCounter(game) == zoneCounter - && affectedControllerId.equals(source.getControllerId()); + return false; + } + + @Override + public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) { + return mana.getFirstAvailable(); } } @@ -240,12 +250,10 @@ class MnemonicBetrayalReturnEffect extends OneShotEffect { return false; } Cards cardsToReturn = new CardsImpl(); - for (Card card : cards.getCards(game)) { - if (game.getState().getZone(card.getId()) == Zone.EXILED - && card.getZoneChangeCounter(game) == cardMap.getOrDefault(card.getId(), -5) + 1) { - cardsToReturn.add(card); - } - } + cards.getCards(game).stream().filter((card) -> (game.getState().getZone(card.getId()) == Zone.EXILED + && card.getZoneChangeCounter(game) == cardMap.getOrDefault(card.getId(), -5) + 1)).forEachOrdered((card) -> { + cardsToReturn.add(card); + }); return player.moveCards(cardsToReturn, Zone.GRAVEYARD, source, game); } } From 0eedca5283ab9a7ff8de0b7e5a571bc10210db7b Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 19 Nov 2018 15:09:36 -0600 Subject: [PATCH 24/61] - Fixed #5393 --- .../mage/cards/a/AminatouTheFateShifter.java | 22 +++++++++++-------- .../src/mage/cards/t/TheEldestReborn.java | 9 ++++---- .../mage/abilities/common/SagaAbility.java | 4 ++-- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AminatouTheFateShifter.java b/Mage.Sets/src/mage/cards/a/AminatouTheFateShifter.java index 8067fc0183c..23a6897b4b9 100644 --- a/Mage.Sets/src/mage/cards/a/AminatouTheFateShifter.java +++ b/Mage.Sets/src/mage/cards/a/AminatouTheFateShifter.java @@ -44,7 +44,7 @@ public class AminatouTheFateShifter extends CardImpl { } public AminatouTheFateShifter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{W}{U}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{W}{U}{B}"); this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.AMINATOU); @@ -56,7 +56,7 @@ public class AminatouTheFateShifter extends CardImpl { // -1: Exile another target permanent you own, then return it to the battlefield under your control. ability = new LoyaltyAbility(new ExileTargetForSourceEffect(), -1); - ability.addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect()); + ability.addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(true)); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); @@ -68,6 +68,7 @@ public class AminatouTheFateShifter extends CardImpl { // Aminatou, the Fateshifter can be your commander. this.addAbility(CanBeYourCommanderAbility.getInstance()); } + public AminatouTheFateShifter(final AminatouTheFateShifter card) { super(card); } @@ -79,6 +80,7 @@ public class AminatouTheFateShifter extends CardImpl { } class AminatouPlusEffect extends OneShotEffect { + public AminatouPlusEffect() { super(Outcome.DrawCard); staticText = "draw a card, then put a card from your hand on top of your library"; @@ -118,10 +120,11 @@ class AminatouPlusEffect extends OneShotEffect { } class AminatouUltimateEffect extends OneShotEffect { - public AminatouUltimateEffect (){ + + public AminatouUltimateEffect() { super(Outcome.Benefit); - staticText = "Choose left or right. Each player gains control of all nonland permanents other than Aminatou," + - " the Fateshifter controlled by the next player in the chosen direction."; + staticText = "Choose left or right. Each player gains control of all nonland permanents other than Aminatou," + + " the Fateshifter controlled by the next player in the chosen direction."; } public AminatouUltimateEffect(final AminatouUltimateEffect effect) { @@ -129,7 +132,9 @@ class AminatouUltimateEffect extends OneShotEffect { } @Override - public AminatouUltimateEffect copy(){return new AminatouUltimateEffect(this);} + public AminatouUltimateEffect copy() { + return new AminatouUltimateEffect(this); + } @Override public boolean apply(Game game, Ability source) { @@ -154,7 +159,7 @@ class AminatouUltimateEffect extends OneShotEffect { return false; } // skip players out of range - if (!game.getState().getPlayersInRange(controller.getId(), game).contains(nextPlayer)){ + if (!game.getState().getPlayersInRange(controller.getId(), game).contains(nextPlayer)) { continue; } // save first next player to check for iteration stop @@ -164,7 +169,7 @@ class AminatouUltimateEffect extends OneShotEffect { FilterNonlandPermanent nextPlayerNonlandPermanentsFilter = new FilterNonlandPermanent(); nextPlayerNonlandPermanentsFilter.add(new ControllerIdPredicate(nextPlayer)); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(nextPlayerNonlandPermanentsFilter, game)) { - if (permanent.getId().equals(source.getSourceId())){ + if (permanent.getId().equals(source.getSourceId())) { continue; } ContinuousEffect effect = new GainControlTargetEffect(Duration.EndOfGame, currentPlayer); @@ -188,4 +193,3 @@ class AminatouUltimateEffect extends OneShotEffect { return nextPlayerId; } } - diff --git a/Mage.Sets/src/mage/cards/t/TheEldestReborn.java b/Mage.Sets/src/mage/cards/t/TheEldestReborn.java index 666e52d657f..c8120d042e3 100644 --- a/Mage.Sets/src/mage/cards/t/TheEldestReborn.java +++ b/Mage.Sets/src/mage/cards/t/TheEldestReborn.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -13,7 +12,6 @@ import mage.constants.SagaChapter; import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.FilterCard; -import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; @@ -35,11 +33,12 @@ public final class TheEldestReborn extends CardImpl { } private static final FilterControlledPermanent filterSacrifice = new FilterControlledPermanent("creature or planeswalker"); + static { filterSacrifice.add(Predicates.or( - new CardTypePredicate(CardType.CREATURE), - new CardTypePredicate(CardType.PLANESWALKER) - )); + new CardTypePredicate(CardType.CREATURE), + new CardTypePredicate(CardType.PLANESWALKER) + )); } diff --git a/Mage/src/main/java/mage/abilities/common/SagaAbility.java b/Mage/src/main/java/mage/abilities/common/SagaAbility.java index 954ed8930d6..624221c13ef 100644 --- a/Mage/src/main/java/mage/abilities/common/SagaAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SagaAbility.java @@ -1,4 +1,3 @@ - package mage.abilities.common; import mage.abilities.Ability; @@ -138,7 +137,8 @@ class ChapterTriggeredAbility extends TriggeredAbilityImpl { if (event.getTargetId().equals(getSourceId()) && event.getData().equals(CounterType.LORE.getName())) { int amountAdded = event.getAmount(); int loreCounters = amountAdded; - Permanent sourceSaga = game.getPermanentOrLKIBattlefield(getSourceId()); + //Permanent sourceSaga = game.getPermanentOrLKIBattlefield(getSourceId()); BUG #5393 + Permanent sourceSaga = game.getPermanent(getSourceId()); if (sourceSaga == null) { sourceSaga = game.getPermanentEntering(getSourceId()); } From 9916dbdad7b59c43e58e71f691267eea619dceaf Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 19 Nov 2018 16:55:46 -0600 Subject: [PATCH 25/61] - Fixed #5434 --- .../src/mage/cards/a/AngelOfSerenity.java | 121 ++---------------- 1 file changed, 14 insertions(+), 107 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java b/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java index b34df8718c7..4dfb63bbc40 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java @@ -1,43 +1,34 @@ - package mage.cards.a; import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; -import mage.abilities.common.ZoneChangeTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnFromExileForSourceEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardIdPredicate; import mage.target.Target; -import mage.target.common.TargetCardInGraveyard; -import mage.target.common.TargetCreaturePermanent; -import mage.util.CardUtil; +import mage.target.common.TargetCardInGraveyardOrBattlefield; /** * * @author LevelX2 */ public final class AngelOfSerenity extends CardImpl { + + private static final String rule = "you may exile up to three other target creatures from the battlefield and/or creature cards from graveyards."; public AngelOfSerenity(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{W}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}{W}"); this.subtype.add(SubType.ANGEL); this.power = new MageInt(5); @@ -47,7 +38,12 @@ public final class AngelOfSerenity extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Angel of Serenity enters the battlefield, you may exile up to three other target creatures from the battlefield and/or creature cards from graveyards. - this.addAbility(new AngelOfSerenityTriggeredAbility()); + FilterCreatureCard filter = new FilterCreatureCard("creatures from the battlefield and/or a graveyard"); + filter.add(Predicates.not(new CardIdPredicate(this.getId()))); + Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect().setText(rule), true); + Target target = new TargetCardInGraveyardOrBattlefield(0, 3, filter); + ability.addTarget(target); + this.addAbility(ability); // When Angel of Serenity leaves the battlefield, return the exiled cards to their owners' hands. this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.HAND, false, true), false)); @@ -62,92 +58,3 @@ public final class AngelOfSerenity extends CardImpl { return new AngelOfSerenity(this); } } - -class AngelOfSerenityTriggeredAbility extends ZoneChangeTriggeredAbility { - - public AngelOfSerenityTriggeredAbility() { - super(Zone.BATTLEFIELD, new AngelOfSerenityEnterEffect(), "When {this} enters the battlefield, ", true); - } - - public AngelOfSerenityTriggeredAbility(AngelOfSerenityTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (super.checkTrigger(event, game)) { - getTargets().clear(); - FilterCreaturePermanent filter = new FilterCreaturePermanent("up to three other target creatures"); - filter.add(new AnotherPredicate()); - TargetCreaturePermanent target1 = new TargetCreaturePermanent(0, 3, filter, false); - game.getPlayer(getControllerId()).chooseTarget(Outcome.Exile, target1, this, game); - if (!target1.getTargets().isEmpty()) { - getTargets().add(target1); - - } - int leftTargets = 3 - target1.getTargets().size(); - if (leftTargets > 0) { - FilterCard filter2 = new FilterCreatureCard("up to " + leftTargets + " target creature card" + (leftTargets > 1 ? "s" : "") + " from graveyards"); - TargetCardInGraveyard target2 = new TargetCardInGraveyard(0, leftTargets, filter2); - game.getPlayer(getControllerId()).chooseTarget(Outcome.Exile, target2, this, game); - if (!target2.getTargets().isEmpty()) { - getTargets().add(target2); - } - } - return true; - } - return false; - } - - @Override - public AngelOfSerenityTriggeredAbility copy() { - return new AngelOfSerenityTriggeredAbility(this); - } - -} - -class AngelOfSerenityEnterEffect extends OneShotEffect { - - public AngelOfSerenityEnterEffect() { - super(Outcome.ReturnToHand); - this.staticText = "you may exile up to three other target creatures from the battlefield and/or creature cards from graveyards"; - } - - public AngelOfSerenityEnterEffect(final AngelOfSerenityEnterEffect effect) { - super(effect); - } - - @Override - public AngelOfSerenityEnterEffect copy() { - return new AngelOfSerenityEnterEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - boolean result = true; - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && sourceObject != null && !source.getTargets().isEmpty()) { - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - for (Target target : source.getTargets()) { - if (target instanceof TargetCreaturePermanent) { - for (UUID permanentId : target.getTargets()) { - Permanent permanent = game.getPermanent(permanentId); - if (permanent != null) { - result |= controller.moveCardToExileWithInfo(permanent, exileZoneId, sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true); - } - } - - } else if (target instanceof TargetCardInGraveyard) { - for (UUID cardId : target.getTargets()) { - Card card = game.getCard(cardId); - if (card != null) { - result |= controller.moveCardToExileWithInfo(card, exileZoneId, sourceObject.getIdName(), source.getSourceId(), game, Zone.GRAVEYARD, true); - } - } - } - } - } - return result; - } -} From 1bac7fc04cb743c7c2463878051d97b1ffef3c99 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 20 Nov 2018 09:46:36 -0600 Subject: [PATCH 26/61] - Fixed Soul Strings. DoUnlessAnyPlayerPays now supports X costs. --- .../java/mage/player/ai/ComputerPlayer.java | 13 +++++ Mage.Sets/src/mage/cards/s/SoulStrings.java | 5 +- Mage.Sets/src/mage/cards/v/ViashinoBey.java | 4 +- Mage.Sets/src/mage/cards/w/WarsToll.java | 5 +- .../common/DoUnlessAnyPlayerPaysEffect.java | 51 +++++++++++++++---- 5 files changed, 62 insertions(+), 16 deletions(-) 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 4ebe78b127a..3625c367692 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 @@ -861,6 +861,19 @@ public class ComputerPlayer extends PlayerImpl implements Player { } return target.isChosen(); } + + if (target.getOriginalTarget() instanceof TargetCardInGraveyardOrBattlefield) { + List cards = new ArrayList<>(); + for (Player player : game.getPlayers().values()) { + cards.addAll(player.getGraveyard().getCards(game)); + cards.addAll(game.getBattlefield().getAllActivePermanents(new FilterPermanent(), player.getId(), game)); + } + Card card = pickTarget(cards, outcome, target, source, game); + if (card != null) { + target.addTarget(card.getId(), source, game); + return true; + } + } throw new IllegalStateException("Target wasn't handled. class:" + target.getClass().toString()); } //end of chooseTarget method diff --git a/Mage.Sets/src/mage/cards/s/SoulStrings.java b/Mage.Sets/src/mage/cards/s/SoulStrings.java index 553e607203a..4dc5d880609 100644 --- a/Mage.Sets/src/mage/cards/s/SoulStrings.java +++ b/Mage.Sets/src/mage/cards/s/SoulStrings.java @@ -1,6 +1,5 @@ package mage.cards.s; -import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DoUnlessAnyPlayerPaysEffect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; @@ -11,6 +10,7 @@ import mage.filter.common.FilterCreatureCard; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; +import mage.abilities.dynamicvalue.common.ManacostVariableValue; /** * @author jmharmon @@ -23,8 +23,7 @@ public final class SoulStrings extends CardImpl { // Return two target creature cards from your graveyard to your hand unless any player pays {X}. Effect effect = new DoUnlessAnyPlayerPaysEffect( - new ReturnFromGraveyardToHandTargetEffect(), new VariableManaCost() - ); + new ReturnFromGraveyardToHandTargetEffect(), new ManacostVariableValue()); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(2, new FilterCreatureCard("creature cards from your graveyard"))); } diff --git a/Mage.Sets/src/mage/cards/v/ViashinoBey.java b/Mage.Sets/src/mage/cards/v/ViashinoBey.java index e0a3e05bd11..d3471e16e40 100644 --- a/Mage.Sets/src/mage/cards/v/ViashinoBey.java +++ b/Mage.Sets/src/mage/cards/v/ViashinoBey.java @@ -79,7 +79,9 @@ class ViashinoBeyEffect extends OneShotEffect { } else { targetDefender.add(game.getOpponents(controller.getId()).iterator().next(), game); } - controller.declareAttacker(permanent.getId(), targetDefender.getFirstTarget(), game, false); + if (permanent.canAttack(targetDefender.getFirstTarget(), game)) { + controller.declareAttacker(permanent.getId(), targetDefender.getFirstTarget(), game, false); + } }); } return false; diff --git a/Mage.Sets/src/mage/cards/w/WarsToll.java b/Mage.Sets/src/mage/cards/w/WarsToll.java index 01972bac1a8..3f103a26978 100644 --- a/Mage.Sets/src/mage/cards/w/WarsToll.java +++ b/Mage.Sets/src/mage/cards/w/WarsToll.java @@ -1,4 +1,3 @@ - package mage.cards.w; import java.util.UUID; @@ -103,7 +102,9 @@ class WarsTollEffect extends OneShotEffect { filterOpponentCreatures.add(new ControllerIdPredicate(opponent.getId())); game.getBattlefield().getAllActivePermanents(CardType.CREATURE).stream().filter((permanent) -> (filterOpponentCreatures.match(permanent, source.getSourceId(), source.getControllerId(), game))).forEachOrdered((permanent) -> { //TODO: allow the player to choose between a planeswalker and player - opponent.declareAttacker(permanent.getId(), source.getControllerId(), game, false); + if (permanent.canAttack(source.getControllerId(), game)) { + opponent.declareAttacker(permanent.getId(), source.getControllerId(), game, false); + } }); return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoUnlessAnyPlayerPaysEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DoUnlessAnyPlayerPaysEffect.java index 4c2208e00c6..8368248a156 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DoUnlessAnyPlayerPaysEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DoUnlessAnyPlayerPaysEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common; import java.util.UUID; @@ -6,6 +5,8 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; @@ -22,15 +23,22 @@ import mage.util.CardUtil; public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect { protected Effects executingEffects = new Effects(); - private final Cost cost; + protected Cost cost; private String chooseUseText; + protected DynamicValue genericMana; + + public DoUnlessAnyPlayerPaysEffect(Effect effect, DynamicValue genericMana) { + super(Outcome.Detriment); + this.genericMana = genericMana; + this.executingEffects.add(effect); + } public DoUnlessAnyPlayerPaysEffect(Effect effect, Cost cost) { this(effect, cost, null); } public DoUnlessAnyPlayerPaysEffect(Effect effect, Cost cost, String chooseUseText) { - super(Outcome.Benefit); + super(Outcome.Neutral); this.executingEffects.add(effect); this.cost = cost; this.chooseUseText = chooseUseText; @@ -38,8 +46,13 @@ public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect { public DoUnlessAnyPlayerPaysEffect(final DoUnlessAnyPlayerPaysEffect effect) { super(effect); + if (effect.cost != null) { + this.cost = effect.cost.copy(); + } + if (effect.genericMana != null) { + this.genericMana = effect.genericMana.copy(); + } this.executingEffects = effect.executingEffects.copy(); - this.cost = effect.cost.copy(); this.chooseUseText = effect.chooseUseText; } @@ -51,11 +64,18 @@ public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && sourceObject != null) { + Cost costToPay; + if (controller != null + && sourceObject != null) { + if (cost != null) { + costToPay = cost.copy(); + } else { + costToPay = new GenericManaCost(genericMana.calculate(game, source, this)); + } String message; if (chooseUseText == null) { String effectText = executingEffects.getText(source.getModes().getMode()); - message = "Pay " + cost.getText() + " to prevent (" + effectText.substring(0, effectText.length() - 1) + ")?"; + message = "Pay " + costToPay.getText() + " to prevent (" + effectText.substring(0, effectText.length() - 1) + ")?"; } else { message = chooseUseText; } @@ -65,9 +85,10 @@ public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect { // check if any player is willing to pay for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); - if (player != null && cost.canPay(source, source.getSourceId(), player.getId(), game) && player.chooseUse(Outcome.Detriment, message, source, game)) { - cost.clearPaid(); - if (cost.pay(source, game, source.getSourceId(), player.getId(), false, null)) { + if (player != null + && costToPay.canPay(source, source.getSourceId(), player.getId(), game) && player.chooseUse(Outcome.Detriment, message, source, game)) { + costToPay.clearPaid(); + if (costToPay.pay(source, game, source.getSourceId(), player.getId(), false, null)) { if (!game.isSimulation()) { game.informPlayers(player.getLogName() + " pays the cost to prevent the effect"); } @@ -100,8 +121,18 @@ public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect { if (!staticText.isEmpty()) { return staticText; } + StringBuilder sb = new StringBuilder(); + if (cost != null) { + sb.append(cost.getText()); + } else { + sb.append("{X}"); + } + if (genericMana != null && !genericMana.getMessage().isEmpty()) { + sb.append(", where X is "); + sb.append(genericMana.getMessage()); + } String effectsText = executingEffects.getText(mode); - return effectsText.substring(0, effectsText.length() - 1) + " unless any player pays " + cost.getText(); + return effectsText.substring(0, effectsText.length() - 1) + " unless any player pays " + sb.toString(); } @Override From c61badc2c37f86bbea7359d2a82cf47f11aaea91 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 20 Nov 2018 15:22:58 -0600 Subject: [PATCH 27/61] - Fixed Stadium Vendors. --- .../src/mage/cards/s/StadiumVendors.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/StadiumVendors.java b/Mage.Sets/src/mage/cards/s/StadiumVendors.java index 04ebe8dad82..c95788ca696 100644 --- a/Mage.Sets/src/mage/cards/s/StadiumVendors.java +++ b/Mage.Sets/src/mage/cards/s/StadiumVendors.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -65,21 +64,22 @@ class StadiumVendorsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getSourceId()); + Player controller = game.getPlayer(source.getControllerId()); if (controller == null) { return false; } TargetPlayer target = new TargetPlayer(1, 1, true); - if (!controller.choose(Outcome.Benefit, target, source.getSourceId(), game)) { - return false; + if (controller.choose(Outcome.Benefit, target, source.getSourceId(), game)) { + Player player = game.getPlayer(target.getFirstTarget()); + ChoiceColor colorChoice = new ChoiceColor(true); + if (player == null + || !player.choose(Outcome.Benefit, colorChoice, game)) { + return false; + } + Effect effect = new AddManaToManaPoolTargetControllerEffect(colorChoice.getMana(2), "that player's"); + effect.setTargetPointer(new FixedTarget(player.getId(), game)); + return effect.apply(game, source); } - Player player = game.getPlayer(target.getFirstTarget()); - ChoiceColor colorChoice = new ChoiceColor(true); - if (player == null || !player.choose(Outcome.Benefit, colorChoice, game)) { - return false; - } - Effect effect = new AddManaToManaPoolTargetControllerEffect(colorChoice.getMana(2), "that player's"); - effect.setTargetPointer(new FixedTarget(player.getId(), game)); - return effect.apply(game, source); + return false; } } From 8185bdf62b7467d1f3b4a0bd2958f5276d4070f4 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 21 Nov 2018 02:54:08 +0400 Subject: [PATCH 28/61] * Updated Canadian Highlander format to November 19th, 2018 version (#5436); --- .../Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java index 89581e8aeb5..da4955be6ab 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/CanadianHighlander.java @@ -45,6 +45,7 @@ public class CanadianHighlander extends Constructed { pointMap.put("Personal Tutor", 1); pointMap.put("Protean Hulk", 3); pointMap.put("Sol Ring", 3); + pointMap.put("Spellseeker", 1); pointMap.put("Stoneforge Mystic", 1); pointMap.put("Strip Mine", 2); pointMap.put("Summoner's Pact", 2); From 2bf0c1d98ef8ceb9af77b22261c6e4bfe02a4347 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 21 Nov 2018 02:57:50 +0400 Subject: [PATCH 29/61] Tests: improved tests performance --- .../base/impl/CardTestPlayerAPIImpl.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index c986d0df0f5..b858d8609b5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -1,11 +1,11 @@ package org.mage.test.serverside.base.impl; -import mage.MageInt; import mage.Mana; import mage.ObjectColor; import mage.abilities.Ability; import mage.cards.Card; import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardLists; import mage.cards.decks.importer.DeckImporterUtil; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; @@ -66,7 +66,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement protected String deckNameD; protected enum ExpectedType { - TURN_NUMBER, RESULT, LIFE, @@ -167,12 +166,21 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement protected TestPlayer createPlayer(Game game, TestPlayer player, String name, String deckName) throws GameException { player = createNewPlayer(name, game.getRangeOfInfluence()); player.setTestMode(true); + logger.debug("Loading deck..."); - Deck deck = Deck.load(DeckImporterUtil.importDeck(deckName), false, false); + DeckCardLists list; + if (loadedDeckCardLists.containsKey(deckName)) { + list = loadedDeckCardLists.get(deckName); + } else { + list = DeckImporterUtil.importDeck(deckName); + loadedDeckCardLists.put(deckName, list); + } + Deck deck = Deck.load(list, false, false); logger.debug("Done!"); if (deck.getCards().size() < 40) { throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); } + game.loadCards(deck.getCards(), player.getId()); game.loadCards(deck.getSideboard(), player.getId()); game.addPlayer(player, deck); From 7c2738cf9f421516f23755bd933716c7c9d4c9d3 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 21 Nov 2018 02:59:24 +0400 Subject: [PATCH 30/61] Merge fix --- .../org/mage/test/serverside/base/MageTestPlayerBase.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java index 37da99ab428..b514cc68b4d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java @@ -10,6 +10,8 @@ import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; import mage.cards.Card; +import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardLists; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import mage.constants.PhaseStep; @@ -54,6 +56,8 @@ public abstract class MageTestPlayerBase { protected Map> commands = new HashMap<>(); + protected static Map loadedDeckCardLists = new HashMap<>(); // test decks buffer + protected TestPlayer playerA; protected TestPlayer playerB; protected TestPlayer playerC; From c7a3e5308371e0cdc831bf6b6d572a098a50c2f4 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 20 Nov 2018 17:43:26 -0600 Subject: [PATCH 31/61] - Fix for Bug #5435. Specific to "must attack" code. --- .../main/java/mage/game/combat/Combat.java | 47 +++++++++++-------- .../main/java/mage/players/PlayerImpl.java | 4 +- .../mage/target/common/TargetDefender.java | 24 ++++++---- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/Mage/src/main/java/mage/game/combat/Combat.java b/Mage/src/main/java/mage/game/combat/Combat.java index 7a1c7a71d8f..08157890e1f 100644 --- a/Mage/src/main/java/mage/game/combat/Combat.java +++ b/Mage/src/main/java/mage/game/combat/Combat.java @@ -1,4 +1,3 @@ - package mage.game.combat; import mage.MageObject; @@ -108,8 +107,8 @@ public class Combat implements Serializable, Copyable { } /** - * Get all possible defender (players and plainwalkers) That does not mean - * neccessarly mean that they are really attacked + * Get all possible defender (players and planeswalkers) That does not mean + * necessarily mean that they are really attacked * * @return */ @@ -251,11 +250,14 @@ public class Combat implements Serializable, Copyable { game.getCombat().checkAttackRequirements(player, game); boolean firstTime = true; do { - if (!firstTime || !game.getPlayer(game.getActivePlayerId()).getAvailableAttackers(game).isEmpty()) { + if (!firstTime + || !game.getPlayer(game.getActivePlayerId()).getAvailableAttackers(game).isEmpty()) { player.selectAttackers(game, attackingPlayerId); } firstTime = false; - if (game.isPaused() || game.checkIfGameIsOver() || game.executingRollback()) { + if (game.isPaused() + || game.checkIfGameIsOver() + || game.executingRollback()) { return; } // because of possible undo during declare attackers it's neccassary to call here the methods with "game.getCombat()." to get the current combat object!!! @@ -338,7 +340,7 @@ public class Combat implements Serializable, Copyable { || !player.chooseUse(Outcome.Benefit, "Do you wish to " + (isBanded ? "band " + attacker.getLogName() + " with another " : "form a band with " + attacker.getLogName() + " and an ") + "attacking creature?", null, game)) { break; } - + if (canBand && canBandWithOther) { if (player.chooseUse(Outcome.Detriment, "Choose type of banding ability to apply:", attacker.getLogName(), "Banding", "Bands with other", null, game)) { canBandWithOther = false; @@ -359,13 +361,13 @@ public class Combat implements Serializable, Copyable { filter.add(Predicates.or(predicates)); } } - + if (target.choose(Outcome.Benefit, attackingPlayerId, null, game)) { isBanded = true; for (UUID targetId : target.getTargets()) { Permanent permanent = game.getPermanent(targetId); if (permanent != null) { - + for (UUID bandedId : attacker.getBandedCards()) { permanent.addBandedCard(bandedId); Permanent banded = game.getPermanent(bandedId); @@ -391,7 +393,7 @@ public class Combat implements Serializable, Copyable { canBand = false; } } - + } } } @@ -454,11 +456,16 @@ public class Combat implements Serializable, Copyable { if (defenders.size() == 1) { player.declareAttacker(creature.getId(), defenders.iterator().next(), game, false); } else { - TargetDefender target = new TargetDefender(defenders, creature.getId()); - target.setRequired(true); - target.setTargetName("planeswalker or player for " + creature.getLogName() + " to attack"); - if (player.chooseTarget(Outcome.Damage, target, null, game)) { - player.declareAttacker(creature.getId(), target.getFirstTarget(), game, false); + if (!player.isHuman()) { // computer only for multiple defenders + player.declareAttacker(creature.getId(), defenders.iterator().next(), game, false); + } else { // human players only for multiple defenders + TargetDefender target = new TargetDefender(defenders, creature.getId()); + target.setRequired(true); + target.setTargetName("planeswalker or player for " + creature.getLogName() + " to attack"); + if (player.chooseTarget(Outcome.Damage, target, null, game)) { + System.out.println("The player " + player.getName() + " declares an attacker here. " + creature.getName()); + player.declareAttacker(creature.getId(), target.getFirstTarget(), game, false); + } } } } else { @@ -550,7 +557,7 @@ public class Combat implements Serializable, Copyable { * Handle the blocker selection process * * @param blockController player that controlls how to block, if null the - * defender is the controller + * defender is the controller * @param game */ public void selectBlockers(Player blockController, Game game) { @@ -1342,10 +1349,10 @@ public class Combat implements Serializable, Copyable { if (defenderAttackedBy.size() >= defendingPlayer.getMaxAttackedBy()) { Player attackingPlayer = game.getPlayer(game.getControllerId(attackerId)); if (attackingPlayer != null && !game.isSimulation()) { - game.informPlayer(attackingPlayer, "No more than " + - CardUtil.numberToText(defendingPlayer.getMaxAttackedBy()) + - " creatures can attack " + - defendingPlayer.getLogName()); + game.informPlayer(attackingPlayer, "No more than " + + CardUtil.numberToText(defendingPlayer.getMaxAttackedBy()) + + " creatures can attack " + + defendingPlayer.getLogName()); } return false; } @@ -1375,7 +1382,7 @@ public class Combat implements Serializable, Copyable { * @param playerId * @param game * @param solveBanding check whether also add creatures banded with - * attackerId + * attackerId */ public void addBlockingGroup(UUID blockerId, UUID attackerId, UUID playerId, Game game, boolean solveBanding) { Permanent blocker = game.getPermanent(blockerId); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index a3ad8068160..ec355521d91 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2372,7 +2372,9 @@ public abstract class PlayerImpl implements Player, Serializable { setStoredBookmark(game.bookmarkState()); // makes it possible to UNDO a declared attacker with costs from e.g. Propaganda } Permanent attacker = game.getPermanent(attackerId); - if (attacker != null && attacker.canAttack(defenderId, game) && attacker.isControlledBy(playerId)) { + if (attacker != null + && attacker.canAttack(defenderId, game) + && attacker.isControlledBy(playerId)) { if (!game.getCombat().declareAttacker(attackerId, defenderId, playerId, game)) { game.undo(playerId); } diff --git a/Mage/src/main/java/mage/target/common/TargetDefender.java b/Mage/src/main/java/mage/target/common/TargetDefender.java index 3d48481d59a..863c419b28a 100644 --- a/Mage/src/main/java/mage/target/common/TargetDefender.java +++ b/Mage/src/main/java/mage/target/common/TargetDefender.java @@ -68,7 +68,8 @@ public class TargetDefender extends TargetImpl { } } for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, sourceControllerId, game)) { - if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) + if ((notTarget + || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(permanent, game)) { count++; if (count >= this.minNumberOfTargets) { @@ -84,7 +85,8 @@ public class TargetDefender extends TargetImpl { int count = 0; for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null && filter.match(player, game)) { + if (player != null + && filter.match(player, game)) { count++; if (count >= this.minNumberOfTargets) { return true; @@ -109,13 +111,15 @@ public class TargetDefender extends TargetImpl { for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); if (player != null - && (notTarget || player.canBeTargetedBy(targetSource, sourceControllerId, game)) + && (notTarget + || player.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(player, game)) { possibleTargets.add(playerId); } } for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, sourceControllerId, game)) { - if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) + if ((notTarget + || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(permanent, game)) { possibleTargets.add(permanent.getId()); } @@ -128,7 +132,8 @@ public class TargetDefender extends TargetImpl { Set possibleTargets = new HashSet<>(); for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null && filter.match(player, game)) { + if (player != null + && filter.match(player, game)) { possibleTargets.add(playerId); } } @@ -162,7 +167,8 @@ public class TargetDefender extends TargetImpl { return filter.match(player, game); } Permanent permanent = game.getPermanent(id); - return permanent != null && filter.match(permanent, game); + return permanent != null + && filter.match(permanent, game); } @Override @@ -170,7 +176,8 @@ public class TargetDefender extends TargetImpl { Player player = game.getPlayer(id); MageObject targetSource = game.getObject(attackerId); if (player != null) { - return (notTarget || player.canBeTargetedBy(targetSource, (source == null ? null : source.getControllerId()), game)) + return (notTarget + || player.canBeTargetedBy(targetSource, (source == null ? null : source.getControllerId()), game)) && filter.match(player, game); } Permanent permanent = game.getPermanent(id); // planeswalker @@ -180,7 +187,8 @@ public class TargetDefender extends TargetImpl { if (source != null) { controllerId = source.getControllerId(); } - return (notTarget || permanent.canBeTargetedBy(targetSource, controllerId, game)) + return (notTarget + || permanent.canBeTargetedBy(targetSource, controllerId, game)) && filter.match(permanent, game); } return false; From 738cb59eaf59b2ff523e8978b40a6a724542de46 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Thu, 22 Nov 2018 19:17:18 +0400 Subject: [PATCH 32/61] Added all cards to Ultimate Masters --- Mage.Sets/src/mage/sets/UltimateMasters.java | 214 ++++++++++++++++++- 1 file changed, 213 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/UltimateMasters.java b/Mage.Sets/src/mage/sets/UltimateMasters.java index a97affe33b6..7e39b5da524 100644 --- a/Mage.Sets/src/mage/sets/UltimateMasters.java +++ b/Mage.Sets/src/mage/sets/UltimateMasters.java @@ -1,4 +1,3 @@ - package mage.sets; import mage.cards.ExpansionSet; @@ -27,46 +26,259 @@ public final class UltimateMasters extends ExpansionSet { this.numBoosterRare = 1; this.ratioBoosterMythic = 8; + cards.add(new SetCardInfo("Aethersnipe", 44, Rarity.COMMON, mage.cards.a.Aethersnipe.class)); + cards.add(new SetCardInfo("Akroan Crusader", 121, Rarity.COMMON, mage.cards.a.AkroanCrusader.class)); + cards.add(new SetCardInfo("All Is Dust", 1, Rarity.RARE, mage.cards.a.AllIsDust.class)); + cards.add(new SetCardInfo("Ancestor's Chosen", 9, Rarity.UNCOMMON, mage.cards.a.AncestorsChosen.class)); cards.add(new SetCardInfo("Ancient Tomb", 236, Rarity.RARE, mage.cards.a.AncientTomb.class)); + cards.add(new SetCardInfo("Angel of Despair", 196, Rarity.UNCOMMON, mage.cards.a.AngelOfDespair.class)); + cards.add(new SetCardInfo("Angelic Renewal", 10, Rarity.COMMON, mage.cards.a.AngelicRenewal.class)); + cards.add(new SetCardInfo("Anger", 122, Rarity.UNCOMMON, mage.cards.a.Anger.class)); + cards.add(new SetCardInfo("Appetite for Brains", 83, Rarity.UNCOMMON, mage.cards.a.AppetiteForBrains.class)); + cards.add(new SetCardInfo("Apprentice Necromancer", 84, Rarity.UNCOMMON, mage.cards.a.ApprenticeNecromancer.class)); + cards.add(new SetCardInfo("Archaeomancer", 45, Rarity.COMMON, mage.cards.a.Archaeomancer.class)); + cards.add(new SetCardInfo("Arena Athlete", 123, Rarity.COMMON, mage.cards.a.ArenaAthlete.class)); + cards.add(new SetCardInfo("Artisan of Kozilek", 2, Rarity.UNCOMMON, mage.cards.a.ArtisanOfKozilek.class)); + cards.add(new SetCardInfo("Back to Basics", 46, Rarity.RARE, mage.cards.b.BackToBasics.class)); cards.add(new SetCardInfo("Balefire Dragon", 124, Rarity.MYTHIC, mage.cards.b.BalefireDragon.class)); + cards.add(new SetCardInfo("Basking Rootwalla", 156, Rarity.COMMON, mage.cards.b.BaskingRootwalla.class)); + cards.add(new SetCardInfo("Beckon Apparition", 211, Rarity.COMMON, mage.cards.b.BeckonApparition.class)); + cards.add(new SetCardInfo("Become Immense", 157, Rarity.UNCOMMON, mage.cards.b.BecomeImmense.class)); cards.add(new SetCardInfo("Bitterblossom", 85, Rarity.MYTHIC, mage.cards.b.Bitterblossom.class)); + cards.add(new SetCardInfo("Blast of Genius", 197, Rarity.UNCOMMON, mage.cards.b.BlastOfGenius.class)); + cards.add(new SetCardInfo("Bloodflow Connoisseur", 86, Rarity.COMMON, mage.cards.b.BloodflowConnoisseur.class)); + cards.add(new SetCardInfo("Boar Umbra", 158, Rarity.UNCOMMON, mage.cards.b.BoarUmbra.class)); + cards.add(new SetCardInfo("Boneyard Wurm", 159, Rarity.UNCOMMON, mage.cards.b.BoneyardWurm.class)); + cards.add(new SetCardInfo("Brawn", 160, Rarity.UNCOMMON, mage.cards.b.Brawn.class)); + cards.add(new SetCardInfo("Brazen Scourge", 125, Rarity.UNCOMMON, mage.cards.b.BrazenScourge.class)); + cards.add(new SetCardInfo("Bridge from Below", 87, Rarity.RARE, mage.cards.b.BridgeFromBelow.class)); + cards.add(new SetCardInfo("Buried Alive", 88, Rarity.UNCOMMON, mage.cards.b.BuriedAlive.class)); + cards.add(new SetCardInfo("Canker Abomination", 212, Rarity.COMMON, mage.cards.c.CankerAbomination.class)); + cards.add(new SetCardInfo("Cathodion", 226, Rarity.COMMON, mage.cards.c.Cathodion.class)); cards.add(new SetCardInfo("Cavern of Souls", 237, Rarity.MYTHIC, mage.cards.c.CavernOfSouls.class)); cards.add(new SetCardInfo("Celestial Colonnade", 238, Rarity.RARE, mage.cards.c.CelestialColonnade.class)); + cards.add(new SetCardInfo("Chainer's Edict", 89, Rarity.UNCOMMON, mage.cards.c.ChainersEdict.class)); + cards.add(new SetCardInfo("Circular Logic", 47, Rarity.UNCOMMON, mage.cards.c.CircularLogic.class)); + cards.add(new SetCardInfo("Conflagrate", 126, Rarity.UNCOMMON, mage.cards.c.Conflagrate.class)); + cards.add(new SetCardInfo("Containment Priest", 11, Rarity.RARE, mage.cards.c.ContainmentPriest.class)); + cards.add(new SetCardInfo("Conviction", 12, Rarity.COMMON, mage.cards.c.Conviction.class)); + cards.add(new SetCardInfo("Countersquall", 198, Rarity.UNCOMMON, mage.cards.c.Countersquall.class)); cards.add(new SetCardInfo("Creeping Tar Pit", 239, Rarity.RARE, mage.cards.c.CreepingTarPit.class)); + cards.add(new SetCardInfo("Crow of Dark Tidings", 90, Rarity.COMMON, mage.cards.c.CrowOfDarkTidings.class)); + cards.add(new SetCardInfo("Crushing Canopy", 161, Rarity.COMMON, mage.cards.c.CrushingCanopy.class)); + cards.add(new SetCardInfo("Dakmor Salvage", 240, Rarity.UNCOMMON, mage.cards.d.DakmorSalvage.class)); + cards.add(new SetCardInfo("Dark Dabbling", 91, Rarity.COMMON, mage.cards.d.DarkDabbling.class)); cards.add(new SetCardInfo("Dark Depths", 241, Rarity.MYTHIC, mage.cards.d.DarkDepths.class)); + cards.add(new SetCardInfo("Dawn Charm", 13, Rarity.UNCOMMON, mage.cards.d.DawnCharm.class)); + cards.add(new SetCardInfo("Daybreak Coronet", 14, Rarity.RARE, mage.cards.d.DaybreakCoronet.class)); + cards.add(new SetCardInfo("Death Denied", 92, Rarity.COMMON, mage.cards.d.DeathDenied.class)); + cards.add(new SetCardInfo("Defy Gravity", 48, Rarity.COMMON, mage.cards.d.DefyGravity.class)); cards.add(new SetCardInfo("Demonic Tutor", 93, Rarity.RARE, mage.cards.d.DemonicTutor.class)); + cards.add(new SetCardInfo("Deranged Assistant", 49, Rarity.COMMON, mage.cards.d.DerangedAssistant.class)); + cards.add(new SetCardInfo("Desolate Lighthouse", 242, Rarity.RARE, mage.cards.d.DesolateLighthouse.class)); + cards.add(new SetCardInfo("Desperate Ritual", 127, Rarity.UNCOMMON, mage.cards.d.DesperateRitual.class)); + cards.add(new SetCardInfo("Devoted Druid", 162, Rarity.UNCOMMON, mage.cards.d.DevotedDruid.class)); + cards.add(new SetCardInfo("Dig Through Time", 50, Rarity.RARE, mage.cards.d.DigThroughTime.class)); + cards.add(new SetCardInfo("Dimir Guildmage", 213, Rarity.COMMON, mage.cards.d.DimirGuildmage.class)); + cards.add(new SetCardInfo("Disrupting Shoal", 51, Rarity.RARE, mage.cards.d.DisruptingShoal.class)); + cards.add(new SetCardInfo("Double Cleave", 214, Rarity.COMMON, mage.cards.d.DoubleCleave.class)); + cards.add(new SetCardInfo("Dreamscape Artist", 52, Rarity.UNCOMMON, mage.cards.d.DreamscapeArtist.class)); + cards.add(new SetCardInfo("Eel Umbra", 53, Rarity.COMMON, mage.cards.e.EelUmbra.class)); + cards.add(new SetCardInfo("Eldrazi Conscription", 3, Rarity.RARE, mage.cards.e.EldraziConscription.class)); + cards.add(new SetCardInfo("Emancipation Angel", 15, Rarity.UNCOMMON, mage.cards.e.EmancipationAngel.class)); cards.add(new SetCardInfo("Emrakul, the Aeons Torn", 4, Rarity.MYTHIC, mage.cards.e.EmrakulTheAeonsTorn.class)); cards.add(new SetCardInfo("Engineered Explosives", 227, Rarity.RARE, mage.cards.e.EngineeredExplosives.class)); cards.add(new SetCardInfo("Entomb", 94, Rarity.RARE, mage.cards.e.Entomb.class)); cards.add(new SetCardInfo("Eternal Witness", 163, Rarity.UNCOMMON, mage.cards.e.EternalWitness.class)); + cards.add(new SetCardInfo("Faith's Fetters", 16, Rarity.COMMON, mage.cards.f.FaithsFetters.class)); + cards.add(new SetCardInfo("Faithless Looting", 128, Rarity.COMMON, mage.cards.f.FaithlessLooting.class)); + cards.add(new SetCardInfo("Fauna Shaman", 164, Rarity.RARE, mage.cards.f.FaunaShaman.class)); + cards.add(new SetCardInfo("Fecundity", 165, Rarity.UNCOMMON, mage.cards.f.Fecundity.class)); + cards.add(new SetCardInfo("Fiend Hunter", 17, Rarity.UNCOMMON, mage.cards.f.FiendHunter.class)); + cards.add(new SetCardInfo("Fiery Temper", 129, Rarity.COMMON, mage.cards.f.FieryTemper.class)); + cards.add(new SetCardInfo("Fire // Ice", 225, Rarity.COMMON, mage.cards.f.FireIce.class)); + cards.add(new SetCardInfo("Firewing Phoenix", 130, Rarity.UNCOMMON, mage.cards.f.FirewingPhoenix.class)); + cards.add(new SetCardInfo("Flagstones of Trokair", 243, Rarity.RARE, mage.cards.f.FlagstonesOfTrokair.class)); + cards.add(new SetCardInfo("Flight of Fancy", 54, Rarity.COMMON, mage.cards.f.FlightOfFancy.class)); + cards.add(new SetCardInfo("Foil", 55, Rarity.COMMON, mage.cards.f.Foil.class)); + cards.add(new SetCardInfo("Forbidden Alchemy", 56, Rarity.UNCOMMON, mage.cards.f.ForbiddenAlchemy.class)); + cards.add(new SetCardInfo("Frantic Search", 57, Rarity.COMMON, mage.cards.f.FranticSearch.class)); cards.add(new SetCardInfo("Fulminator Mage", 215, Rarity.RARE, mage.cards.f.FulminatorMage.class)); + cards.add(new SetCardInfo("Fume Spitter", 95, Rarity.COMMON, mage.cards.f.FumeSpitter.class)); + cards.add(new SetCardInfo("Furnace Celebration", 131, Rarity.UNCOMMON, mage.cards.f.FurnaceCelebration.class)); cards.add(new SetCardInfo("Gaddock Teeg", 199, Rarity.RARE, mage.cards.g.GaddockTeeg.class)); + cards.add(new SetCardInfo("Gamble", 132, Rarity.RARE, mage.cards.g.Gamble.class)); + cards.add(new SetCardInfo("Garna, the Bloodflame", 200, Rarity.UNCOMMON, mage.cards.g.GarnaTheBloodflame.class)); + cards.add(new SetCardInfo("Generator Servant", 133, Rarity.COMMON, mage.cards.g.GeneratorServant.class)); + cards.add(new SetCardInfo("Ghoulcaller's Accomplice", 96, Rarity.COMMON, mage.cards.g.GhoulcallersAccomplice.class)); + cards.add(new SetCardInfo("Ghoulsteed", 97, Rarity.UNCOMMON, mage.cards.g.Ghoulsteed.class)); + cards.add(new SetCardInfo("Glen Elendra Archmage", 58, Rarity.RARE, mage.cards.g.GlenElendraArchmage.class)); + cards.add(new SetCardInfo("Gods Willing", 18, Rarity.COMMON, mage.cards.g.GodsWilling.class)); + cards.add(new SetCardInfo("Golgari Brownscale", 166, Rarity.COMMON, mage.cards.g.GolgariBrownscale.class)); + cards.add(new SetCardInfo("Golgari Charm", 201, Rarity.UNCOMMON, mage.cards.g.GolgariCharm.class)); + cards.add(new SetCardInfo("Golgari Grave-Troll", 167, Rarity.RARE, mage.cards.g.GolgariGraveTroll.class)); + cards.add(new SetCardInfo("Golgari Thug", 98, Rarity.UNCOMMON, mage.cards.g.GolgariThug.class)); cards.add(new SetCardInfo("Goryo's Vengeance", 99, Rarity.RARE, mage.cards.g.GoryosVengeance.class)); + cards.add(new SetCardInfo("Grave Scrabbler", 100, Rarity.COMMON, mage.cards.g.GraveScrabbler.class)); + cards.add(new SetCardInfo("Grave Strength", 101, Rarity.UNCOMMON, mage.cards.g.GraveStrength.class)); + cards.add(new SetCardInfo("Groundskeeper", 168, Rarity.COMMON, mage.cards.g.Groundskeeper.class)); + cards.add(new SetCardInfo("Gurmag Angler", 102, Rarity.COMMON, mage.cards.g.GurmagAngler.class)); + cards.add(new SetCardInfo("Heap Doll", 228, Rarity.UNCOMMON, mage.cards.h.HeapDoll.class)); + cards.add(new SetCardInfo("Heliod's Pilgrim", 19, Rarity.COMMON, mage.cards.h.HeliodsPilgrim.class)); + cards.add(new SetCardInfo("Hero of Iroas", 20, Rarity.UNCOMMON, mage.cards.h.HeroOfIroas.class)); + cards.add(new SetCardInfo("Hero of Leina Tower", 169, Rarity.UNCOMMON, mage.cards.h.HeroOfLeinaTower.class)); + cards.add(new SetCardInfo("Hissing Iguanar", 134, Rarity.COMMON, mage.cards.h.HissingIguanar.class)); + cards.add(new SetCardInfo("Hooting Mandrills", 170, Rarity.COMMON, mage.cards.h.HootingMandrills.class)); + cards.add(new SetCardInfo("Hyena Umbra", 21, Rarity.COMMON, mage.cards.h.HyenaUmbra.class)); + cards.add(new SetCardInfo("Icatian Crier", 22, Rarity.COMMON, mage.cards.i.IcatianCrier.class)); + cards.add(new SetCardInfo("Ingot Chewer", 135, Rarity.COMMON, mage.cards.i.IngotChewer.class)); + cards.add(new SetCardInfo("Iridescent Drake", 59, Rarity.UNCOMMON, mage.cards.i.IridescentDrake.class)); + cards.add(new SetCardInfo("Just the Wind", 60, Rarity.COMMON, mage.cards.j.JustTheWind.class)); cards.add(new SetCardInfo("Karakas", 244, Rarity.MYTHIC, mage.cards.k.Karakas.class)); cards.add(new SetCardInfo("Karn Liberated", 5, Rarity.MYTHIC, mage.cards.k.KarnLiberated.class)); cards.add(new SetCardInfo("Kitchen Finks", 216, Rarity.UNCOMMON, mage.cards.k.KitchenFinks.class)); + cards.add(new SetCardInfo("Kodama's Reach", 171, Rarity.COMMON, mage.cards.k.KodamasReach.class)); cards.add(new SetCardInfo("Kozilek, Butcher of Truth", 6, Rarity.MYTHIC, mage.cards.k.KozilekButcherOfTruth.class)); + cards.add(new SetCardInfo("Laboratory Maniac", 61, Rarity.UNCOMMON, mage.cards.l.LaboratoryManiac.class)); + cards.add(new SetCardInfo("Last Gasp", 103, Rarity.COMMON, mage.cards.l.LastGasp.class)); + cards.add(new SetCardInfo("Lava Spike", 136, Rarity.UNCOMMON, mage.cards.l.LavaSpike.class)); cards.add(new SetCardInfo("Lavaclaw Reaches", 245, Rarity.RARE, mage.cards.l.LavaclawReaches.class)); cards.add(new SetCardInfo("Leovold, Emissary of Trest", 202, Rarity.MYTHIC, mage.cards.l.LeovoldEmissaryOfTrest.class)); cards.add(new SetCardInfo("Life from the Loam", 172, Rarity.RARE, mage.cards.l.LifeFromTheLoam.class)); cards.add(new SetCardInfo("Liliana of the Veil", 104, Rarity.MYTHIC, mage.cards.l.LilianaOfTheVeil.class)); + cards.add(new SetCardInfo("Living Lore", 62, Rarity.UNCOMMON, mage.cards.l.LivingLore.class)); cards.add(new SetCardInfo("Lord of Extinction", 203, Rarity.MYTHIC, mage.cards.l.LordOfExtinction.class)); + cards.add(new SetCardInfo("Lotus-Eye Mystics", 23, Rarity.COMMON, mage.cards.l.LotusEyeMystics.class)); + cards.add(new SetCardInfo("Mad Prophet", 137, Rarity.COMMON, mage.cards.m.MadProphet.class)); cards.add(new SetCardInfo("Maelstrom Pulse", 204, Rarity.RARE, mage.cards.m.MaelstromPulse.class)); + cards.add(new SetCardInfo("Mage-Ring Network", 246, Rarity.UNCOMMON, mage.cards.m.MageRingNetwork.class)); + cards.add(new SetCardInfo("Magmaw", 138, Rarity.UNCOMMON, mage.cards.m.Magmaw.class)); + cards.add(new SetCardInfo("Magus of the Bazaar", 63, Rarity.RARE, mage.cards.m.MagusOfTheBazaar.class)); + cards.add(new SetCardInfo("Mahamoti Djinn", 64, Rarity.UNCOMMON, mage.cards.m.MahamotiDjinn.class)); + cards.add(new SetCardInfo("Malevolent Whispers", 139, Rarity.UNCOMMON, mage.cards.m.MalevolentWhispers.class)); + cards.add(new SetCardInfo("Mammoth Umbra", 24, Rarity.COMMON, mage.cards.m.MammothUmbra.class)); cards.add(new SetCardInfo("Mana Vault", 229, Rarity.MYTHIC, mage.cards.m.ManaVault.class)); + cards.add(new SetCardInfo("Marang River Prowler", 65, Rarity.UNCOMMON, mage.cards.m.MarangRiverProwler.class)); + cards.add(new SetCardInfo("Mark of the Vampire", 105, Rarity.COMMON, mage.cards.m.MarkOfTheVampire.class)); + cards.add(new SetCardInfo("Martyr of Sands", 25, Rarity.COMMON, mage.cards.m.MartyrOfSands.class)); cards.add(new SetCardInfo("Mikaeus, the Unhallowed", 106, Rarity.MYTHIC, mage.cards.m.MikaeusTheUnhallowed.class)); + cards.add(new SetCardInfo("Miming Slime", 173, Rarity.COMMON, mage.cards.m.MimingSlime.class)); + cards.add(new SetCardInfo("Miraculous Recovery", 26, Rarity.UNCOMMON, mage.cards.m.MiraculousRecovery.class)); + cards.add(new SetCardInfo("Mistveil Plains", 247, Rarity.UNCOMMON, mage.cards.m.MistveilPlains.class)); + cards.add(new SetCardInfo("Moan of the Unhallowed", 107, Rarity.COMMON, mage.cards.m.MoanOfTheUnhallowed.class)); + cards.add(new SetCardInfo("Molten Birth", 140, Rarity.COMMON, mage.cards.m.MoltenBirth.class)); + cards.add(new SetCardInfo("Murderous Redcap", 217, Rarity.UNCOMMON, mage.cards.m.MurderousRedcap.class)); + cards.add(new SetCardInfo("Myr Servitor", 230, Rarity.COMMON, mage.cards.m.MyrServitor.class)); + cards.add(new SetCardInfo("Mystic Retrieval", 66, Rarity.UNCOMMON, mage.cards.m.MysticRetrieval.class)); + cards.add(new SetCardInfo("Nightbird's Clutches", 141, Rarity.COMMON, mage.cards.n.NightbirdsClutches.class)); cards.add(new SetCardInfo("Noble Hierarch", 174, Rarity.RARE, mage.cards.n.NobleHierarch.class)); + cards.add(new SetCardInfo("Nourishing Shoal", 175, Rarity.RARE, mage.cards.n.NourishingShoal.class)); + cards.add(new SetCardInfo("Offalsnout", 108, Rarity.COMMON, mage.cards.o.Offalsnout.class)); + cards.add(new SetCardInfo("Olivia's Dragoon", 109, Rarity.COMMON, mage.cards.o.OliviasDragoon.class)); + cards.add(new SetCardInfo("Patchwork Gnomes", 231, Rarity.COMMON, mage.cards.p.PatchworkGnomes.class)); + cards.add(new SetCardInfo("Pattern of Rebirth", 176, Rarity.RARE, mage.cards.p.PatternOfRebirth.class)); + cards.add(new SetCardInfo("Penumbra Wurm", 177, Rarity.UNCOMMON, mage.cards.p.PenumbraWurm.class)); + cards.add(new SetCardInfo("Phalanx Leader", 27, Rarity.UNCOMMON, mage.cards.p.PhalanxLeader.class)); + cards.add(new SetCardInfo("Phyrexian Altar", 232, Rarity.RARE, mage.cards.p.PhyrexianAltar.class)); + cards.add(new SetCardInfo("Phyrexian Tower", 248, Rarity.RARE, mage.cards.p.PhyrexianTower.class)); cards.add(new SetCardInfo("Platinum Emperion", 233, Rarity.MYTHIC, mage.cards.p.PlatinumEmperion.class)); + cards.add(new SetCardInfo("Plumeveil", 218, Rarity.UNCOMMON, mage.cards.p.Plumeveil.class)); + cards.add(new SetCardInfo("Prey Upon", 178, Rarity.COMMON, mage.cards.p.PreyUpon.class)); + cards.add(new SetCardInfo("Prismatic Lens", 234, Rarity.UNCOMMON, mage.cards.p.PrismaticLens.class)); + cards.add(new SetCardInfo("Pulse of Murasa", 179, Rarity.COMMON, mage.cards.p.PulseOfMurasa.class)); cards.add(new SetCardInfo("Raging Ravine", 249, Rarity.RARE, mage.cards.r.RagingRavine.class)); + cards.add(new SetCardInfo("Raid Bombardment", 142, Rarity.COMMON, mage.cards.r.RaidBombardment.class)); + cards.add(new SetCardInfo("Rakdos Shred-Freak", 219, Rarity.COMMON, mage.cards.r.RakdosShredFreak.class)); + cards.add(new SetCardInfo("Rally the Peasants", 28, Rarity.UNCOMMON, mage.cards.r.RallyThePeasants.class)); cards.add(new SetCardInfo("Reanimate", 110, Rarity.RARE, mage.cards.r.Reanimate.class)); + cards.add(new SetCardInfo("Reckless Charge", 143, Rarity.COMMON, mage.cards.r.RecklessCharge.class)); + cards.add(new SetCardInfo("Reckless Wurm", 144, Rarity.COMMON, mage.cards.r.RecklessWurm.class)); + cards.add(new SetCardInfo("Repel the Darkness", 29, Rarity.COMMON, mage.cards.r.RepelTheDarkness.class)); + cards.add(new SetCardInfo("Resurrection", 30, Rarity.COMMON, mage.cards.r.Resurrection.class)); + cards.add(new SetCardInfo("Reveillark", 31, Rarity.RARE, mage.cards.r.Reveillark.class)); + cards.add(new SetCardInfo("Reviving Vapors", 205, Rarity.UNCOMMON, mage.cards.r.RevivingVapors.class)); + cards.add(new SetCardInfo("Reya Dawnbringer", 32, Rarity.RARE, mage.cards.r.ReyaDawnbringer.class)); + cards.add(new SetCardInfo("Rise from the Tides", 67, Rarity.UNCOMMON, mage.cards.r.RiseFromTheTides.class)); + cards.add(new SetCardInfo("Rogue's Passage", 250, Rarity.UNCOMMON, mage.cards.r.RoguesPassage.class)); + cards.add(new SetCardInfo("Rolling Temblor", 145, Rarity.UNCOMMON, mage.cards.r.RollingTemblor.class)); + cards.add(new SetCardInfo("Ronom Unicorn", 33, Rarity.COMMON, mage.cards.r.RonomUnicorn.class)); + cards.add(new SetCardInfo("Rune Snag", 68, Rarity.COMMON, mage.cards.r.RuneSnag.class)); + cards.add(new SetCardInfo("Runed Halo", 34, Rarity.RARE, mage.cards.r.RunedHalo.class)); + cards.add(new SetCardInfo("Safehold Elite", 220, Rarity.COMMON, mage.cards.s.SafeholdElite.class)); + cards.add(new SetCardInfo("Sanitarium Skeleton", 111, Rarity.COMMON, mage.cards.s.SanitariumSkeleton.class)); + cards.add(new SetCardInfo("Satyr Wayfinder", 180, Rarity.COMMON, mage.cards.s.SatyrWayfinder.class)); + cards.add(new SetCardInfo("Scuzzback Marauders", 221, Rarity.COMMON, mage.cards.s.ScuzzbackMarauders.class)); + cards.add(new SetCardInfo("Seismic Assault", 146, Rarity.RARE, mage.cards.s.SeismicAssault.class)); + cards.add(new SetCardInfo("Seize the Day", 147, Rarity.RARE, mage.cards.s.SeizeTheDay.class)); + cards.add(new SetCardInfo("Shed Weakness", 181, Rarity.COMMON, mage.cards.s.ShedWeakness.class)); + cards.add(new SetCardInfo("Shielding Plax", 222, Rarity.COMMON, mage.cards.s.ShieldingPlax.class)); + cards.add(new SetCardInfo("Shirei, Shizo's Caretaker", 112, Rarity.UNCOMMON, mage.cards.s.ShireiShizosCaretaker.class)); + cards.add(new SetCardInfo("Shriekmaw", 113, Rarity.UNCOMMON, mage.cards.s.Shriekmaw.class)); cards.add(new SetCardInfo("Sigarda, Host of Herons", 206, Rarity.MYTHIC, mage.cards.s.SigardaHostOfHerons.class)); + cards.add(new SetCardInfo("Sigil of the New Dawn", 35, Rarity.UNCOMMON, mage.cards.s.SigilOfTheNewDawn.class)); + cards.add(new SetCardInfo("Skyspear Cavalry", 36, Rarity.COMMON, mage.cards.s.SkyspearCavalry.class)); + cards.add(new SetCardInfo("Skywing Aven", 69, Rarity.COMMON, mage.cards.s.SkywingAven.class)); + cards.add(new SetCardInfo("Sleight of Hand", 70, Rarity.UNCOMMON, mage.cards.s.SleightOfHand.class)); + cards.add(new SetCardInfo("Slippery Bogle", 223, Rarity.UNCOMMON, mage.cards.s.SlipperyBogle.class)); + cards.add(new SetCardInfo("Slum Reaper", 114, Rarity.COMMON, mage.cards.s.SlumReaper.class)); + cards.add(new SetCardInfo("Snake Umbra", 182, Rarity.UNCOMMON, mage.cards.s.SnakeUmbra.class)); cards.add(new SetCardInfo("Snapcaster Mage", 71, Rarity.MYTHIC, mage.cards.s.SnapcasterMage.class)); + cards.add(new SetCardInfo("Songs of the Damned", 115, Rarity.UNCOMMON, mage.cards.s.SongsOfTheDamned.class)); + cards.add(new SetCardInfo("Soul's Fire", 148, Rarity.COMMON, mage.cards.s.SoulsFire.class)); + cards.add(new SetCardInfo("Sovereigns of Lost Alara", 207, Rarity.RARE, mage.cards.s.SovereignsOfLostAlara.class)); + cards.add(new SetCardInfo("Sparkspitter", 149, Rarity.COMMON, mage.cards.s.Sparkspitter.class)); + cards.add(new SetCardInfo("Spider Spawning", 183, Rarity.UNCOMMON, mage.cards.s.SpiderSpawning.class)); + cards.add(new SetCardInfo("Spider Umbra", 184, Rarity.COMMON, mage.cards.s.SpiderUmbra.class)); + cards.add(new SetCardInfo("Spirit Cairn", 37, Rarity.UNCOMMON, mage.cards.s.SpiritCairn.class)); + cards.add(new SetCardInfo("Spoils of the Vault", 116, Rarity.RARE, mage.cards.s.SpoilsOfTheVault.class)); + cards.add(new SetCardInfo("Squee, Goblin Nabob", 150, Rarity.RARE, mage.cards.s.SqueeGoblinNabob.class)); + cards.add(new SetCardInfo("Staunch-Hearted Warrior", 185, Rarity.COMMON, mage.cards.s.StaunchHeartedWarrior.class)); + cards.add(new SetCardInfo("Stingerfling Spider", 186, Rarity.UNCOMMON, mage.cards.s.StingerflingSpider.class)); cards.add(new SetCardInfo("Stirring Wildwood", 251, Rarity.RARE, mage.cards.s.StirringWildwood.class)); + cards.add(new SetCardInfo("Stitched Drake", 72, Rarity.COMMON, mage.cards.s.StitchedDrake.class)); + cards.add(new SetCardInfo("Stitcher's Apprentice", 73, Rarity.COMMON, mage.cards.s.StitchersApprentice.class)); + cards.add(new SetCardInfo("Stream of Consciousness", 74, Rarity.UNCOMMON, mage.cards.s.StreamOfConsciousness.class)); + cards.add(new SetCardInfo("Sublime Archangel", 38, Rarity.RARE, mage.cards.s.SublimeArchangel.class)); + cards.add(new SetCardInfo("Sultai Skullkeeper", 75, Rarity.COMMON, mage.cards.s.SultaiSkullkeeper.class)); + cards.add(new SetCardInfo("Swift Reckoning", 39, Rarity.UNCOMMON, mage.cards.s.SwiftReckoning.class)); + cards.add(new SetCardInfo("Talrand, Sky Summoner", 76, Rarity.RARE, mage.cards.t.TalrandSkySummoner.class)); cards.add(new SetCardInfo("Tarmogoyf", 187, Rarity.MYTHIC, mage.cards.t.Tarmogoyf.class)); cards.add(new SetCardInfo("Tasigur, the Golden Fang", 117, Rarity.RARE, mage.cards.t.TasigurTheGoldenFang.class)); cards.add(new SetCardInfo("Temporal Manipulation", 77, Rarity.MYTHIC, mage.cards.t.TemporalManipulation.class)); + cards.add(new SetCardInfo("Terramorphic Expanse", 252, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class)); + cards.add(new SetCardInfo("Tethmos High Priest", 40, Rarity.COMMON, mage.cards.t.TethmosHighPriest.class)); + cards.add(new SetCardInfo("Thermo-Alchemist", 151, Rarity.COMMON, mage.cards.t.ThermoAlchemist.class)); + cards.add(new SetCardInfo("Thespian's Stage", 253, Rarity.RARE, mage.cards.t.ThespiansStage.class)); + cards.add(new SetCardInfo("Think Twice", 78, Rarity.COMMON, mage.cards.t.ThinkTwice.class)); cards.add(new SetCardInfo("Through the Breach", 152, Rarity.RARE, mage.cards.t.ThroughTheBreach.class)); + cards.add(new SetCardInfo("Travel Preparations", 188, Rarity.UNCOMMON, mage.cards.t.TravelPreparations.class)); + cards.add(new SetCardInfo("Treasure Cruise", 79, Rarity.COMMON, mage.cards.t.TreasureCruise.class)); + cards.add(new SetCardInfo("Turn to Mist", 224, Rarity.COMMON, mage.cards.t.TurnToMist.class)); + cards.add(new SetCardInfo("Twins of Maurer Estate", 118, Rarity.COMMON, mage.cards.t.TwinsOfMaurerEstate.class)); + cards.add(new SetCardInfo("Ulamog's Crusher", 8, Rarity.COMMON, mage.cards.u.UlamogsCrusher.class)); cards.add(new SetCardInfo("Ulamog, the Infinite Gyre", 7, Rarity.MYTHIC, mage.cards.u.UlamogTheInfiniteGyre.class)); + cards.add(new SetCardInfo("Unburial Rites", 119, Rarity.UNCOMMON, mage.cards.u.UnburialRites.class)); + cards.add(new SetCardInfo("Undying Rage", 153, Rarity.COMMON, mage.cards.u.UndyingRage.class)); + cards.add(new SetCardInfo("Unholy Hunger", 120, Rarity.COMMON, mage.cards.u.UnholyHunger.class)); + cards.add(new SetCardInfo("Unstable Mutation", 80, Rarity.UNCOMMON, mage.cards.u.UnstableMutation.class)); + cards.add(new SetCardInfo("Urban Evolution", 208, Rarity.UNCOMMON, mage.cards.u.UrbanEvolution.class)); cards.add(new SetCardInfo("Urborg, Tomb of Yawgmoth", 254, Rarity.RARE, mage.cards.u.UrborgTombOfYawgmoth.class)); + cards.add(new SetCardInfo("Vengeful Rebirth", 209, Rarity.UNCOMMON, mage.cards.v.VengefulRebirth.class)); cards.add(new SetCardInfo("Vengevine", 189, Rarity.MYTHIC, mage.cards.v.Vengevine.class)); + cards.add(new SetCardInfo("Verdant Eidolon", 190, Rarity.COMMON, mage.cards.v.VerdantEidolon.class)); + cards.add(new SetCardInfo("Vessel of Endless Rest", 235, Rarity.COMMON, mage.cards.v.VesselOfEndlessRest.class)); + cards.add(new SetCardInfo("Vexing Devil", 154, Rarity.RARE, mage.cards.v.VexingDevil.class)); + cards.add(new SetCardInfo("Visions of Beyond", 81, Rarity.RARE, mage.cards.v.VisionsOfBeyond.class)); + cards.add(new SetCardInfo("Walker of the Grove", 191, Rarity.COMMON, mage.cards.w.WalkerOfTheGrove.class)); + cards.add(new SetCardInfo("Wall of Reverence", 41, Rarity.RARE, mage.cards.w.WallOfReverence.class)); + cards.add(new SetCardInfo("Wandering Champion", 42, Rarity.COMMON, mage.cards.w.WanderingChampion.class)); + cards.add(new SetCardInfo("Warleader's Helix", 210, Rarity.UNCOMMON, mage.cards.w.WarleadersHelix.class)); + cards.add(new SetCardInfo("Whirlwind Adept", 82, Rarity.COMMON, mage.cards.w.WhirlwindAdept.class)); + cards.add(new SetCardInfo("Wickerbough Elder", 192, Rarity.COMMON, mage.cards.w.WickerboughElder.class)); + cards.add(new SetCardInfo("Wild Hunger", 193, Rarity.UNCOMMON, mage.cards.w.WildHunger.class)); + cards.add(new SetCardInfo("Wild Mongrel", 194, Rarity.COMMON, mage.cards.w.WildMongrel.class)); + cards.add(new SetCardInfo("Wingsteed Rider", 43, Rarity.COMMON, mage.cards.w.WingsteedRider.class)); + cards.add(new SetCardInfo("Woodfall Primus", 195, Rarity.RARE, mage.cards.w.WoodfallPrimus.class)); + cards.add(new SetCardInfo("Young Pyromancer", 155, Rarity.UNCOMMON, mage.cards.y.YoungPyromancer.class)); } } From 57d5c391fa23d748b8d4df4f92e46359c389b6c1 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 22 Nov 2018 16:22:07 +0100 Subject: [PATCH 33/61] * Adroit Hateflayer - Fixed triggered ability only doing damage to opponents instead of all players (#5416). --- Mage.Sets/src/mage/cards/a/AdroitHateflayer.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AdroitHateflayer.java b/Mage.Sets/src/mage/cards/a/AdroitHateflayer.java index e51f259f632..dcf0a7f023a 100644 --- a/Mage.Sets/src/mage/cards/a/AdroitHateflayer.java +++ b/Mage.Sets/src/mage/cards/a/AdroitHateflayer.java @@ -1,10 +1,9 @@ - package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.effects.common.LoseLifeAllPlayersEffect; import mage.abilities.keyword.MenaceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -18,7 +17,7 @@ import mage.constants.SubType; public final class AdroitHateflayer extends CardImpl { public AdroitHateflayer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{U}{B}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{B}{R}"); this.subtype.add(SubType.NAUTOLAN); this.subtype.add(SubType.SITH); this.power = new MageInt(3); @@ -26,9 +25,9 @@ public final class AdroitHateflayer extends CardImpl { // Menace this.addAbility(new MenaceAbility()); - - // Whenever Adroit Hateflayer attacks, each opponent loses 2 life. - this.addAbility(new AttacksTriggeredAbility(new LoseLifeOpponentsEffect(2), false)); + + // Whenever Adroit Hateflayer attacks, each player loses 2 life. + this.addAbility(new AttacksTriggeredAbility(new LoseLifeAllPlayersEffect(2), false)); } public AdroitHateflayer(final AdroitHateflayer card) { From dbbe92ff0a363eb41a1369353abe57cff1a65300 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 23 Nov 2018 10:59:04 +0100 Subject: [PATCH 34/61] * Outer Rim Slaver - Fixed rule text. Improved target description text (#5416). --- Mage.Sets/src/mage/cards/o/OuterRimSlaver.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/cards/o/OuterRimSlaver.java b/Mage.Sets/src/mage/cards/o/OuterRimSlaver.java index 98ea2f4b723..438ca1a7292 100644 --- a/Mage.Sets/src/mage/cards/o/OuterRimSlaver.java +++ b/Mage.Sets/src/mage/cards/o/OuterRimSlaver.java @@ -1,4 +1,3 @@ - package mage.cards.o; import java.util.UUID; @@ -30,12 +29,13 @@ public final class OuterRimSlaver extends CardImpl { this.toughness = new MageInt(3); // When Outer Rim Slaver enters the battlefield, you may put a bounty counter on target creature. If you do, another target creature fights that creature - Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.BOUNTY.createInstance()), true); - ability.addEffect(new FightTargetsEffect("another target creature fights that creature")); - TargetCreaturePermanent target = new TargetCreaturePermanent(); + Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.BOUNTY.createInstance()) + .setText("you may put a bounty counter on target creature"), true); + ability.addEffect(new FightTargetsEffect("If you do, another target creature fights that creature")); + TargetCreaturePermanent target = new TargetCreaturePermanent(new FilterCreaturePermanent("creature to put a bounty counter on it")); target.setTargetTag(1); ability.addTarget(target); - FilterCreaturePermanent filter = new FilterCreaturePermanent(); + FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature to fight that creature that gets the bounty counter"); filter.add(new AnotherTargetPredicate(2)); TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter); target2.setTargetTag(2); From 43ee5e9c43dc6ae9c48d301abdf8792b7b9c79f3 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 23 Nov 2018 12:30:44 +0100 Subject: [PATCH 35/61] * Acklay of the Arena - Fixed triggered ability (fixes #5416). --- Mage.Sets/src/mage/cards/g/GhostQuarter.java | 4 +- .../effects/common/FightTargetsEffect.java | 57 +++++++++++++------ 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GhostQuarter.java b/Mage.Sets/src/mage/cards/g/GhostQuarter.java index b84b4a3ba4d..66a20c6860e 100644 --- a/Mage.Sets/src/mage/cards/g/GhostQuarter.java +++ b/Mage.Sets/src/mage/cards/g/GhostQuarter.java @@ -1,4 +1,3 @@ - package mage.cards.g; import java.util.UUID; @@ -16,7 +15,6 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterBasicLandCard; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -30,7 +28,7 @@ import mage.target.common.TargetLandPermanent; public final class GhostQuarter extends CardImpl { public GhostQuarter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java index c0fd2f1ca51..db8b80f874c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/FightTargetsEffect.java @@ -1,6 +1,6 @@ - package mage.abilities.effects.common; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; @@ -32,35 +32,58 @@ public class FightTargetsEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Card card = game.getCard(source.getSourceId()); if (card != null) { - // only if both targets are legal the effect will be applied - if (source.getTargets().get(0).isLegal(source, game) && source.getTargets().get(1).isLegal(source, game)) { - Permanent creature1 = game.getPermanent(source.getTargets().get(0).getFirstTarget()); - Permanent creature2 = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - // 20110930 - 701.10 - if (creature1 != null && creature2 != null) { - if (creature1.isCreature() && creature2.isCreature()) { - return creature1.fight(creature2, source, game); - } + UUID target1Id = null; + UUID target2Id = null; + // first target is in target pointer, second target is a normal target + if (source.getTargets().size() < 2) { + if (!source.getTargets().get(0).isLegal(source, game)) { + return false; + } + target1Id = getTargetPointer().getFirst(game, source); + target2Id = source.getTargets().getFirstTarget(); + if (target1Id == target2Id) { + return false; + } + // two normal targets available, only if both targets are legal the effect will be applied + } else if (source.getTargets().get(0).isLegal(source, game) && source.getTargets().get(1).isLegal(source, game)) { + target1Id = source.getTargets().get(0).getFirstTarget(); + target2Id = source.getTargets().get(1).getFirstTarget(); + } + Permanent creature1 = game.getPermanent(target1Id); + Permanent creature2 = game.getPermanent(target2Id); + // 20110930 - 701.10 + if (creature1 != null && creature2 != null) { + if (creature1.isCreature() && creature2.isCreature()) { + return creature1.fight(creature2, source, game); } } - if (!game.isSimulation()) { - game.informPlayers(card.getName() + " has been fizzled."); - } + } + if (!game.isSimulation()) { + game.informPlayers(card.getName() + " has been fizzled."); } return false; } @Override - public FightTargetsEffect copy() { + public FightTargetsEffect + copy() { return new FightTargetsEffect(this); + } @Override - public String getText(Mode mode) { - if (staticText != null && !staticText.isEmpty()) { + public String + getText(Mode mode + ) { + if (staticText + != null && !staticText + .isEmpty()) { return staticText; + } - return "Target " + mode.getTargets().get(0).getTargetName() + " fights another target " + mode.getTargets().get(1).getTargetName(); + return "Target " + mode + .getTargets().get(0).getTargetName() + " fights another target " + mode + .getTargets().get(1).getTargetName(); } } From 2e17cb334e445b6f2dd2df33338a925036af6c3c Mon Sep 17 00:00:00 2001 From: jeffwadsworth Date: Fri, 23 Nov 2018 11:36:29 -0600 Subject: [PATCH 36/61] - Fixed #5100. Added a replacement effect for skipping the combat phase. --- Mage.Sets/src/mage/cards/b/BlindingAngel.java | 22 ++++---- Mage.Sets/src/mage/cards/e/EmptyCityRuse.java | 10 ++-- Mage.Sets/src/mage/cards/f/FalsePeace.java | 14 ++--- .../src/mage/cards/m/MomentOfSilence.java | 14 ++--- .../src/mage/cards/r/RevenantPatriarch.java | 14 ++--- .../src/mage/cards/s/StonehornDignitary.java | 18 +++--- .../effects/common/SkipCombatStepEffect.java | 55 +++++++++++++++++++ 7 files changed, 102 insertions(+), 45 deletions(-) create mode 100644 Mage/src/main/java/mage/abilities/effects/common/SkipCombatStepEffect.java diff --git a/Mage.Sets/src/mage/cards/b/BlindingAngel.java b/Mage.Sets/src/mage/cards/b/BlindingAngel.java index c75b26c07e6..f7f25c29ab0 100644 --- a/Mage.Sets/src/mage/cards/b/BlindingAngel.java +++ b/Mage.Sets/src/mage/cards/b/BlindingAngel.java @@ -1,14 +1,14 @@ - package mage.cards.b; import java.util.UUID; import mage.MageInt; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.common.SkipNextCombatEffect; +import mage.abilities.effects.common.SkipCombatStepEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.SubType; /** @@ -16,26 +16,28 @@ import mage.constants.SubType; * @author Plopman */ public final class BlindingAngel extends CardImpl { - + public BlindingAngel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); this.subtype.add(SubType.ANGEL); - + this.power = new MageInt(2); this.toughness = new MageInt(4); // Flying this.addAbility(FlyingAbility.getInstance()); - // Whenever Blinding Angel deals combat damage to a player, that player skips their next combat phase. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new SkipNextCombatEffect(), false, true)); - } + // Whenever Blinding Angel deals combat damage to a player, that player skips their next combat phase. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new SkipCombatStepEffect(Duration.OneUse).setText("that player skips their next combat phase."), false, true)); + + } + public BlindingAngel(final BlindingAngel card) { super(card); } - + @Override public BlindingAngel copy() { return new BlindingAngel(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/e/EmptyCityRuse.java b/Mage.Sets/src/mage/cards/e/EmptyCityRuse.java index 020899fc0ea..8e1ff3e4178 100644 --- a/Mage.Sets/src/mage/cards/e/EmptyCityRuse.java +++ b/Mage.Sets/src/mage/cards/e/EmptyCityRuse.java @@ -1,11 +1,11 @@ - package mage.cards.e; import java.util.UUID; -import mage.abilities.effects.common.SkipNextCombatEffect; +import mage.abilities.effects.common.SkipCombatStepEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.target.common.TargetOpponent; /** @@ -15,10 +15,10 @@ import mage.target.common.TargetOpponent; public final class EmptyCityRuse extends CardImpl { public EmptyCityRuse(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{W}"); // Target opponent skips all combat phases of their next turn. - this.getSpellAbility().addEffect(new SkipNextCombatEffect()); + this.getSpellAbility().addEffect(new SkipCombatStepEffect(Duration.UntilYourNextTurn).setText("Target opponent skips all combat phases of their next turn.")); this.getSpellAbility().addTarget(new TargetOpponent()); } @@ -30,4 +30,4 @@ public final class EmptyCityRuse extends CardImpl { public EmptyCityRuse copy() { return new EmptyCityRuse(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/f/FalsePeace.java b/Mage.Sets/src/mage/cards/f/FalsePeace.java index 026dae7c19a..55bfe14023c 100644 --- a/Mage.Sets/src/mage/cards/f/FalsePeace.java +++ b/Mage.Sets/src/mage/cards/f/FalsePeace.java @@ -1,11 +1,11 @@ - package mage.cards.f; import java.util.UUID; -import mage.abilities.effects.common.SkipNextCombatEffect; +import mage.abilities.effects.common.SkipCombatStepEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.target.TargetPlayer; /** @@ -13,19 +13,19 @@ import mage.target.TargetPlayer; * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class FalsePeace extends CardImpl { - + public FalsePeace(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{W}"); // Target player skips all combat phases of their next turn. - this.getSpellAbility().addEffect(new SkipNextCombatEffect()); + this.getSpellAbility().addEffect(new SkipCombatStepEffect(Duration.UntilYourNextTurn).setText("Target player skips all combat phases of their next turn.")); this.getSpellAbility().addTarget(new TargetPlayer()); } - + public FalsePeace(final FalsePeace card) { super(card); } - + @Override public FalsePeace copy() { return new FalsePeace(this); diff --git a/Mage.Sets/src/mage/cards/m/MomentOfSilence.java b/Mage.Sets/src/mage/cards/m/MomentOfSilence.java index 50e275a7ed0..6607ae3528b 100644 --- a/Mage.Sets/src/mage/cards/m/MomentOfSilence.java +++ b/Mage.Sets/src/mage/cards/m/MomentOfSilence.java @@ -1,11 +1,11 @@ - package mage.cards.m; import java.util.UUID; -import mage.abilities.effects.common.SkipNextCombatEffect; +import mage.abilities.effects.common.SkipCombatStepEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.target.TargetPlayer; /** @@ -13,19 +13,19 @@ import mage.target.TargetPlayer; * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class MomentOfSilence extends CardImpl { - + public MomentOfSilence(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); // Target player skips their next combat phase this turn. - this.getSpellAbility().addEffect(new SkipNextCombatEffect()); + this.getSpellAbility().addEffect(new SkipCombatStepEffect(Duration.EndOfTurn).setText("Target player skips their next combat this turn")); this.getSpellAbility().addTarget(new TargetPlayer()); } - + public MomentOfSilence(final MomentOfSilence card) { super(card); } - + @Override public MomentOfSilence copy() { return new MomentOfSilence(this); diff --git a/Mage.Sets/src/mage/cards/r/RevenantPatriarch.java b/Mage.Sets/src/mage/cards/r/RevenantPatriarch.java index 50b1fd07e85..e770b5d4397 100644 --- a/Mage.Sets/src/mage/cards/r/RevenantPatriarch.java +++ b/Mage.Sets/src/mage/cards/r/RevenantPatriarch.java @@ -1,4 +1,3 @@ - package mage.cards.r; import java.util.UUID; @@ -8,12 +7,13 @@ import mage.abilities.common.CantBlockAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.ManaWasSpentCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.common.SkipNextCombatEffect; +import mage.abilities.effects.common.SkipCombatStepEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.ColoredManaSymbol; +import mage.constants.Duration; import mage.target.TargetPlayer; import mage.watchers.common.ManaSpentToCastWatcher; @@ -22,26 +22,26 @@ import mage.watchers.common.ManaSpentToCastWatcher; * @author ilcartographer */ public final class RevenantPatriarch extends CardImpl { - + public RevenantPatriarch(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); this.subtype.add(SubType.SPIRIT); this.power = new MageInt(4); this.toughness = new MageInt(3); // When Revenant Patriarch enters the battlefield, if {W} was spent to cast it, target player skips their next combat phase. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new SkipNextCombatEffect(), false); + TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new SkipCombatStepEffect(Duration.OneUse).setText("target player skips their next combat phase."), false); ability.addTarget(new TargetPlayer()); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new ManaWasSpentCondition(ColoredManaSymbol.W), "if {W} was spent to cast it, target player skips their next combat phase."), new ManaSpentToCastWatcher()); // Revenant Patriarch can't block. this.addAbility(new CantBlockAbility()); } - + public RevenantPatriarch(final RevenantPatriarch card) { super(card); } - + @Override public RevenantPatriarch copy() { return new RevenantPatriarch(this); diff --git a/Mage.Sets/src/mage/cards/s/StonehornDignitary.java b/Mage.Sets/src/mage/cards/s/StonehornDignitary.java index 3f92110a139..0443f4f16db 100644 --- a/Mage.Sets/src/mage/cards/s/StonehornDignitary.java +++ b/Mage.Sets/src/mage/cards/s/StonehornDignitary.java @@ -1,14 +1,14 @@ - package mage.cards.s; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.common.SkipNextCombatEffect; +import mage.abilities.effects.common.SkipCombatStepEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.SubType; import mage.target.common.TargetOpponent; @@ -17,27 +17,27 @@ import mage.target.common.TargetOpponent; * @author nantuko */ public final class StonehornDignitary extends CardImpl { - + public StonehornDignitary(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.RHINO); this.subtype.add(SubType.SOLDIER); - + this.power = new MageInt(1); this.toughness = new MageInt(4); // When Stonehorn Dignitary enters the battlefield, target opponent skips their next combat phase. - Ability ability = new EntersBattlefieldTriggeredAbility(new SkipNextCombatEffect()); + Ability ability = new EntersBattlefieldTriggeredAbility(new SkipCombatStepEffect(Duration.OneUse).setText("target opponent skips their next combat phase.")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } - + public StonehornDignitary(final StonehornDignitary card) { super(card); } - + @Override public StonehornDignitary copy() { return new StonehornDignitary(this); } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/SkipCombatStepEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SkipCombatStepEffect.java new file mode 100644 index 00000000000..4d72fe19fd0 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/SkipCombatStepEffect.java @@ -0,0 +1,55 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author jeffwadsworth + */ + +public class SkipCombatStepEffect extends ReplacementEffectImpl { + + public SkipCombatStepEffect(Duration duration) { + super(duration, Outcome.Detriment); + staticText = "that player skips their next combat phase"; + } + + public SkipCombatStepEffect(final SkipCombatStepEffect effect) { + super(effect); + } + + @Override + public SkipCombatStepEffect copy() { + return new SkipCombatStepEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COMBAT_PHASE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getPlayerId().equals(targetPointer.getFirst(game, source)); + } +} From d9b25cbac1e5c55733618c0058ee29750f11b493 Mon Sep 17 00:00:00 2001 From: Ingmar Goudt Date: Fri, 23 Nov 2018 19:21:03 +0100 Subject: [PATCH 37/61] suppert mtgjson v4 --- .../src/main/java/mage/verify/Booster.java | 8 ++++ .../main/java/mage/verify/ForeignData.java | 12 ++++++ .../src/main/java/mage/verify/JsonCard.java | 19 +++++++++ .../src/main/java/mage/verify/JsonSet.java | 12 ++++++ .../src/main/java/mage/verify/Legality.java | 19 +++++++++ .../src/main/java/mage/verify/Meta.java | 6 +++ .../src/main/java/mage/verify/MtgJson.java | 16 ++++++-- .../src/main/java/mage/verify/Ruling.java | 6 +++ .../src/main/java/mage/verify/Token.java | 19 +++++++++ .../java/mage/verify/VerifyCardDataTest.java | 40 ++++++++++--------- 10 files changed, 136 insertions(+), 21 deletions(-) create mode 100644 Mage.Verify/src/main/java/mage/verify/Booster.java create mode 100644 Mage.Verify/src/main/java/mage/verify/ForeignData.java create mode 100644 Mage.Verify/src/main/java/mage/verify/Legality.java create mode 100644 Mage.Verify/src/main/java/mage/verify/Meta.java create mode 100644 Mage.Verify/src/main/java/mage/verify/Ruling.java create mode 100644 Mage.Verify/src/main/java/mage/verify/Token.java diff --git a/Mage.Verify/src/main/java/mage/verify/Booster.java b/Mage.Verify/src/main/java/mage/verify/Booster.java new file mode 100644 index 00000000000..161dcfff1d2 --- /dev/null +++ b/Mage.Verify/src/main/java/mage/verify/Booster.java @@ -0,0 +1,8 @@ +package mage.verify; + +public class Booster { + + public Booster(String mythic){ + + } +} diff --git a/Mage.Verify/src/main/java/mage/verify/ForeignData.java b/Mage.Verify/src/main/java/mage/verify/ForeignData.java new file mode 100644 index 00000000000..e410fb24d73 --- /dev/null +++ b/Mage.Verify/src/main/java/mage/verify/ForeignData.java @@ -0,0 +1,12 @@ +package mage.verify; + +public class ForeignData { + + + public String language; + public String name; + public String type; + public String text; + public String flavorText; + public String multiverseId; +} diff --git a/Mage.Verify/src/main/java/mage/verify/JsonCard.java b/Mage.Verify/src/main/java/mage/verify/JsonCard.java index a17d1a7b6bb..c7a0c49a4b5 100644 --- a/Mage.Verify/src/main/java/mage/verify/JsonCard.java +++ b/Mage.Verify/src/main/java/mage/verify/JsonCard.java @@ -3,10 +3,23 @@ package mage.verify; import java.util.List; class JsonCard { + public String uuid; + public String convertedManaCost; + public List foreignData; + public boolean isReserved; + public String side; + public Legality legalities; + public List printings; + public List rulings; + public List colorIndicator; public String layout; public String name; public List names; // flip cards public String manaCost; + public boolean hasFoil; + public boolean hasNonFoil; + public String multiverseId; + public String frameVersion; public int cmc; public List colors; public List colorIdentity; @@ -22,6 +35,10 @@ class JsonCard { public boolean starter; // only available in boxed sets and not in boosters public int hand; // vanguard public int life; // vanguard + public String originalText; + public String originalType; + public String flavorText; + public boolean isOnlineOnly; // only available in AllSets.json public String artist; @@ -37,4 +54,6 @@ class JsonCard { public String border; public String watermark; public boolean timeshifted; + public String borderColor; + public boolean isOversized; } diff --git a/Mage.Verify/src/main/java/mage/verify/JsonSet.java b/Mage.Verify/src/main/java/mage/verify/JsonSet.java index 831721937fd..466ebbfb630 100644 --- a/Mage.Verify/src/main/java/mage/verify/JsonSet.java +++ b/Mage.Verify/src/main/java/mage/verify/JsonSet.java @@ -1,5 +1,7 @@ package mage.verify; +import com.fasterxml.jackson.annotation.JsonIgnore; + import java.util.List; import java.util.Map; @@ -21,4 +23,14 @@ class JsonSet { public String mkm_id; public String mkm_name; public Map translations; + public int baseSetSize; + @JsonIgnore + public List boosterV3; + public String borderColor; + public Meta meta; + public String mtgoCode; + public List tokens; + public int totalSetSize; + public boolean isOnlineOnly; + public boolean isFoilOnly; } diff --git a/Mage.Verify/src/main/java/mage/verify/Legality.java b/Mage.Verify/src/main/java/mage/verify/Legality.java new file mode 100644 index 00000000000..925bc2189e7 --- /dev/null +++ b/Mage.Verify/src/main/java/mage/verify/Legality.java @@ -0,0 +1,19 @@ +package mage.verify; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Legality { + @JsonProperty("1v1") + public String oneVersusOne; + public String commander; + public String duel; + public String legacy; + public String penny; + public String vintage; +public String frontier; +public String modern; +public String pauper; +public String brawl; +public String future; +public String standard; +} diff --git a/Mage.Verify/src/main/java/mage/verify/Meta.java b/Mage.Verify/src/main/java/mage/verify/Meta.java new file mode 100644 index 00000000000..4a93171da0d --- /dev/null +++ b/Mage.Verify/src/main/java/mage/verify/Meta.java @@ -0,0 +1,6 @@ +package mage.verify; + +public class Meta { + public String date; + public String version; +} diff --git a/Mage.Verify/src/main/java/mage/verify/MtgJson.java b/Mage.Verify/src/main/java/mage/verify/MtgJson.java index 1919f8db0f8..4301ac0b6cb 100644 --- a/Mage.Verify/src/main/java/mage/verify/MtgJson.java +++ b/Mage.Verify/src/main/java/mage/verify/MtgJson.java @@ -12,8 +12,7 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.text.Normalizer; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.zip.ZipInputStream; public final class MtgJson { @@ -61,6 +60,16 @@ public final class MtgJson { static { try { cards = loadAllCards(); + List oldKeys = new ArrayList<>(); + Map newKeys = new HashMap<>(); + for (String key : cards.keySet()) { + if (key.contains("(")) { + newKeys.put(key.replaceAll("\\(.*\\)", "").trim(), cards.get(key)); + oldKeys.add(key); + } + } + cards.putAll(newKeys); + cards.keySet().removeAll(oldKeys); addAliases(cards); } catch (IOException e) { throw new RuntimeException(e); @@ -95,7 +104,7 @@ public final class MtgJson { if (stream == null) { File file = new File(filename); if (!file.exists()) { - InputStream download = new URL("http://mtgjson.com/json/" + filename).openStream(); + InputStream download = new URL("http://mtgjson.com/v4/json/" + filename).openStream(); Files.copy(download, file.toPath(), StandardCopyOption.REPLACE_EXISTING); System.out.println("Downloaded " + filename + " to " + file.getAbsolutePath()); } else { @@ -133,6 +142,7 @@ public final class MtgJson { name = name.replace("'", "\""); // for Kongming, "Sleeping Dragon" & Pang Tong, "Young Phoenix" ref = reference.get(name); } + return ref; } diff --git a/Mage.Verify/src/main/java/mage/verify/Ruling.java b/Mage.Verify/src/main/java/mage/verify/Ruling.java new file mode 100644 index 00000000000..5bcb036329c --- /dev/null +++ b/Mage.Verify/src/main/java/mage/verify/Ruling.java @@ -0,0 +1,6 @@ +package mage.verify; + +public class Ruling { + public String text; + public String date; +} diff --git a/Mage.Verify/src/main/java/mage/verify/Token.java b/Mage.Verify/src/main/java/mage/verify/Token.java new file mode 100644 index 00000000000..c99ed0abc25 --- /dev/null +++ b/Mage.Verify/src/main/java/mage/verify/Token.java @@ -0,0 +1,19 @@ +package mage.verify; + +import java.util.List; + +public class Token { + public String artist; + public String borderColor; + public List colorIdentity; + public List colors; + public String name; + public String number; + public String power; + public String toughness; + public List reverseRelated; + public String text; + public String type; + public String uuid; + public String watermark; +} diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index 991315438a3..fb16264f15f 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -1,14 +1,13 @@ package mage.verify; import mage.ObjectColor; -import mage.abilities.Ability; +import mage.abilities.keyword.DevoidAbility; import mage.abilities.keyword.MultikickerAbility; import mage.cards.*; import mage.cards.basiclands.BasicLand; -import mage.cards.repository.CardRepository; -import mage.cards.repository.CardScanner; import mage.constants.CardType; import mage.constants.Rarity; +import mage.constants.SubType; import mage.constants.SuperType; import mage.game.permanent.token.Token; import mage.game.permanent.token.TokenImpl; @@ -232,7 +231,7 @@ public class VerifyCardDataTest { // replace codes for aliases String searchSet = MtgJson.mtgJsonToXMageCodes.getOrDefault(refSet.code, refSet.code); - ExpansionSet mageSet = Sets.findSet(searchSet); + ExpansionSet mageSet = Sets.findSet(searchSet.toUpperCase()); if (mageSet == null) { totalMissingSets = totalMissingSets + 1; totalMissingCards = totalMissingCards + refSet.cards.size(); @@ -383,7 +382,7 @@ public class VerifyCardDataTest { // check for (ExpansionSet.SetCardInfo card : set.getSetCardInfo()) { boolean cardHaveDoubleName = (doubleNames.getOrDefault(card.getName(), 0) > 1); - boolean cardHaveVariousSetting = card.getGraphicInfo() == null ? false : card.getGraphicInfo().getUsesVariousArt(); + boolean cardHaveVariousSetting = card.getGraphicInfo() != null && card.getGraphicInfo().getUsesVariousArt(); if (cardHaveDoubleName && !cardHaveVariousSetting) { errorsList.add("error, founded double card names, but UsesVariousArt is not true: " + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber()); @@ -569,17 +568,22 @@ public class VerifyCardDataTest { return; } - Collection expected = ref.colors; - ObjectColor color = card.getColor(null); - if (expected == null) { - expected = Collections.emptyList(); + Set expected = new HashSet<>(); + if (ref.colors != null) { + expected.addAll(ref.colors); } + if(card.isFlipCard()){ + expected.addAll(ref.colorIdentity); + } + + ObjectColor color = card.getColor(null); + if (expected.size() != color.getColorCount() - || (color.isBlack() && !expected.contains("Black")) - || (color.isBlue() && !expected.contains("Blue")) - || (color.isGreen() && !expected.contains("Green")) - || (color.isRed() && !expected.contains("Red")) - || (color.isWhite() && !expected.contains("White"))) { + || (color.isBlack() && !expected.contains("B")) + || (color.isBlue() && !expected.contains("U")) + || (color.isGreen() && !expected.contains("G")) + || (color.isRed() && !expected.contains("R")) + || (color.isWhite() && !expected.contains("W"))) { fail(card, "colors", color + " != " + expected); } } @@ -601,7 +605,7 @@ public class VerifyCardDataTest { } } - if (!eqSet(card.getSubtype(null).stream().map(p -> p.toString()).collect(Collectors.toSet()), expected)) { + if (!eqSet(card.getSubtype(null).stream().map(SubType::toString).collect(Collectors.toSet()), expected)) { fail(card, "subtypes", card.getSubtype(null) + " != " + expected); } } @@ -628,11 +632,11 @@ public class VerifyCardDataTest { } // special check: kicker ability must be in rules - if (card.getAbilities().containsClass(MultikickerAbility.class) && !card.getRules().stream().anyMatch(rule -> rule.contains("Multikicker"))) { + if (card.getAbilities().containsClass(MultikickerAbility.class) && card.getRules().stream().noneMatch(rule -> rule.contains("Multikicker"))) { fail(card, "abilities", "card have Multikicker ability, but missing it in rules text"); } - // spells have only 1 abilities + // spells have only 1 ability if (card.isSorcery() || card.isInstant()) { return; } @@ -697,7 +701,7 @@ public class VerifyCardDataTest { String expected = ref.manaCost; String cost = join(card.getManaCost().getSymbols()); - if (cost != null && cost.isEmpty()) { + if (cost.isEmpty()) { cost = null; } if (cost != null) { From ed69c0449014e37f4ab2496ed665b0ca8b0e5f0d Mon Sep 17 00:00:00 2001 From: Ingmar Goudt Date: Fri, 23 Nov 2018 20:05:30 +0100 Subject: [PATCH 38/61] fix for #5417 --- Mage.Sets/src/mage/cards/i/Icequake.java | 14 ++++++----- .../test/cards/conditional/IcequakeTest.java | 25 +++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/conditional/IcequakeTest.java diff --git a/Mage.Sets/src/mage/cards/i/Icequake.java b/Mage.Sets/src/mage/cards/i/Icequake.java index cad1b47cd1d..70399b40e7d 100644 --- a/Mage.Sets/src/mage/cards/i/Icequake.java +++ b/Mage.Sets/src/mage/cards/i/Icequake.java @@ -59,14 +59,16 @@ class IcequakeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (permanent != null && controller != null) { - permanent.destroy(source.getSourceId(), game, false); - if (permanent.isSnow()) { - controller.damage(1, source.getSourceId(), game, false, true); + if (permanent != null) { + Player controller = game.getPlayer(permanent.getControllerId()); + if(controller != null) { + permanent.destroy(source.getSourceId(), game, false); + if (permanent.isSnow()) { + controller.damage(1, source.getSourceId(), game, false, true); + } + return true; } - return true; } return false; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/conditional/IcequakeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/IcequakeTest.java new file mode 100644 index 00000000000..f3db7d63796 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/IcequakeTest.java @@ -0,0 +1,25 @@ +package org.mage.test.cards.conditional; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class IcequakeTest extends CardTestPlayerBase { + + @Test + public void testIcequakeOnSnowLand(){ + + addCard(Zone.BATTLEFIELD, playerB, "Snow-Covered Plains"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp",10); + addCard(Zone.HAND, playerA, "Icequake"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN,playerA, "Icequake", "Snow-Covered Plains"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 19); + } +} From 25e7b53268ec320c09660e8bfd8afcdcd5ce6ebf Mon Sep 17 00:00:00 2001 From: Ingmar Goudt Date: Sat, 24 Nov 2018 23:50:05 +0100 Subject: [PATCH 39/61] small performance update for ClassScanner --- Mage/src/main/java/mage/util/ClassScanner.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Mage/src/main/java/mage/util/ClassScanner.java b/Mage/src/main/java/mage/util/ClassScanner.java index 46609ea4baf..47972fe400b 100644 --- a/Mage/src/main/java/mage/util/ClassScanner.java +++ b/Mage/src/main/java/mage/util/ClassScanner.java @@ -33,8 +33,8 @@ public final class ClassScanner { if(classLoader == null) classLoader = Thread.currentThread().getContextClassLoader(); assert classLoader != null; - HashMap dirs = new HashMap<>(); - TreeSet jars = new TreeSet<>(); + Map dirs = new HashMap<>(); + Set jars = new TreeSet<>(); for (String packageName : packages) { String path = packageName.replace('.', '/'); Enumeration resources = classLoader.getResources(path); @@ -51,8 +51,8 @@ public final class ClassScanner { } } - for (String filePath : dirs.keySet()) { - cards.addAll(findClasses(classLoader, new File(filePath), dirs.get(filePath), type)); + for (Map.Entry dir : dirs.entrySet()) { + cards.addAll(findClasses(classLoader, new File(dir.getKey()), dir.getValue(), type)); } for (String filePath : jars) { @@ -66,7 +66,7 @@ public final class ClassScanner { private static List findClasses(ClassLoader classLoader, File directory, String packageName, Class type) { List cards = new ArrayList<>(); - if (!directory.exists()) return cards; + if (directory == null || !directory.exists()) return cards; for (File file : directory.listFiles()) { if (file.getName().endsWith(".class")) { From 2144ae75a4cabb230353d68ea597cd9b68210c44 Mon Sep 17 00:00:00 2001 From: jeffwadsworth Date: Sun, 25 Nov 2018 10:08:30 -0600 Subject: [PATCH 40/61] - Added Archery Training and Fend Off. --- .../src/mage/cards/a/ArcheryTraining.java | 67 +++++++++++++++++++ Mage.Sets/src/mage/cards/f/FendOff.java | 41 ++++++++++++ Mage.Sets/src/mage/sets/UrzasDestiny.java | 2 + 3 files changed, 110 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/a/ArcheryTraining.java create mode 100644 Mage.Sets/src/mage/cards/f/FendOff.java diff --git a/Mage.Sets/src/mage/cards/a/ArcheryTraining.java b/Mage.Sets/src/mage/cards/a/ArcheryTraining.java new file mode 100644 index 00000000000..f70caaa22c9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArcheryTraining.java @@ -0,0 +1,67 @@ +package mage.cards.a; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.target.common.TargetAttackingOrBlockingCreature; + +/** + * + * @author jeffwadsworth + */ +public final class ArcheryTraining extends CardImpl { + + private static final String rule = "This creature deals X damage to target attacking or blocking creature, where X is the number of arrow counters on {this}."; + + public ArcheryTraining(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // At the beginning of your upkeep, you may put an arrow counter on Archery Training. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.ARROW.createInstance(), true), TargetController.YOU, true)); + + // Enchanted creature has "{tap}: This creature deals X damage to target attacking or blocking creature, where X is the number of arrow counters on Archery Training." + Ability gainedAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new CountersSourceCount(CounterType.ARROW)).setText(rule), new TapSourceCost()); + gainedAbility.addTarget(new TargetAttackingOrBlockingCreature()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA))); + + } + + public ArcheryTraining(final ArcheryTraining card) { + super(card); + } + + @Override + public ArcheryTraining copy() { + return new ArcheryTraining(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FendOff.java b/Mage.Sets/src/mage/cards/f/FendOff.java new file mode 100644 index 00000000000..045d166fe7d --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FendOff.java @@ -0,0 +1,41 @@ +package mage.cards.f; + +import java.util.UUID; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.PreventDamageByTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author jeffwadsworth + */ +public final class FendOff extends CardImpl { + + private static final String rule = "Prevent all combat damage that would be dealt by target creature this turn."; + + public FendOff(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Prevent all combat damage that would be dealt by target creature this turn. + this.getSpellAbility().addEffect(new PreventDamageByTargetEffect(Duration.EndOfTurn, true).setText(rule)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + + } + + public FendOff(final FendOff card) { + super(card); + } + + @Override + public FendOff copy() { + return new FendOff(this); + } +} diff --git a/Mage.Sets/src/mage/sets/UrzasDestiny.java b/Mage.Sets/src/mage/sets/UrzasDestiny.java index 810b12ccff6..2590ab67e58 100644 --- a/Mage.Sets/src/mage/sets/UrzasDestiny.java +++ b/Mage.Sets/src/mage/sets/UrzasDestiny.java @@ -32,6 +32,7 @@ public final class UrzasDestiny extends ExpansionSet { cards.add(new SetCardInfo("Aether Sting", 76, Rarity.UNCOMMON, mage.cards.a.AetherSting.class)); cards.add(new SetCardInfo("Ancient Silverback", 101, Rarity.RARE, mage.cards.a.AncientSilverback.class)); cards.add(new SetCardInfo("Apprentice Necromancer", 51, Rarity.RARE, mage.cards.a.ApprenticeNecromancer.class)); + cards.add(new SetCardInfo("Archery Training", 2, Rarity.UNCOMMON, mage.cards.a.ArcheryTraining.class)); cards.add(new SetCardInfo("Attrition", 52, Rarity.RARE, mage.cards.a.Attrition.class)); cards.add(new SetCardInfo("Aura Thief", 26, Rarity.RARE, mage.cards.a.AuraThief.class)); cards.add(new SetCardInfo("Blizzard Elemental", 27, Rarity.RARE, mage.cards.b.BlizzardElemental.class)); @@ -63,6 +64,7 @@ public final class UrzasDestiny extends ExpansionSet { cards.add(new SetCardInfo("Eradicate", 60, Rarity.UNCOMMON, mage.cards.e.Eradicate.class)); cards.add(new SetCardInfo("Extruder", 130, Rarity.UNCOMMON, mage.cards.e.Extruder.class)); cards.add(new SetCardInfo("False Prophet", 6, Rarity.RARE, mage.cards.f.FalseProphet.class)); + cards.add(new SetCardInfo("Fend Off", 7, Rarity.COMMON, mage.cards.f.FendOff.class)); cards.add(new SetCardInfo("Festering Wound", 61, Rarity.UNCOMMON, mage.cards.f.FesteringWound.class)); cards.add(new SetCardInfo("Field Surgeon", 8, Rarity.COMMON, mage.cards.f.FieldSurgeon.class)); cards.add(new SetCardInfo("Flame Jet", 81, Rarity.COMMON, mage.cards.f.FlameJet.class)); From 9c0162caad06038485c74cadcd1850ff406f6cc5 Mon Sep 17 00:00:00 2001 From: jeffwadsworth Date: Sun, 25 Nov 2018 10:11:50 -0600 Subject: [PATCH 41/61] - Added "ARROW" counter. --- Mage/src/main/java/mage/counters/CounterType.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index fd2eb779126..a5ce8b6a603 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -9,6 +9,7 @@ public enum CounterType { AGE("age"), AIM("aim"), + ARROW("arrow"), ARROWHEAD("arrowhead"), AWAKENING("awakening"), BLAZE("blaze"), From ea4c202a40f89ea9911c5b8e4b00a410092bb315 Mon Sep 17 00:00:00 2001 From: jeffwadsworth Date: Sun, 25 Nov 2018 10:51:27 -0600 Subject: [PATCH 42/61] - Added Fatigue and Disappear. --- Mage.Sets/src/mage/cards/d/Disappear.java | 87 +++++++++++++++++++ Mage.Sets/src/mage/cards/f/Fatigue.java | 36 ++++++++ .../common/SkipNextDrawStepTargetEffect.java | 55 ++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/Disappear.java create mode 100644 Mage.Sets/src/mage/cards/f/Fatigue.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/SkipNextDrawStepTargetEffect.java diff --git a/Mage.Sets/src/mage/cards/d/Disappear.java b/Mage.Sets/src/mage/cards/d/Disappear.java new file mode 100644 index 00000000000..43b6fa78a8a --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/Disappear.java @@ -0,0 +1,87 @@ +package mage.cards.d; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author jeffwadsworth + */ +public final class Disappear extends CardImpl { + + public Disappear(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // {U}: Return enchanted creature and Disappear to their owners' hands. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DisappearEffect(), new ManaCostsImpl("{U}"))); + + } + + public Disappear(final Disappear card) { + super(card); + } + + @Override + public Disappear copy() { + return new Disappear(this); + } +} + +class DisappearEffect extends OneShotEffect { + + public DisappearEffect() { + super(Outcome.ReturnToHand); + staticText = "Return enchanted creature and {this} to their owners' hands"; + } + + public DisappearEffect(final DisappearEffect effect) { + super(effect); + } + + @Override + public DisappearEffect copy() { + return new DisappearEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent aura = (Permanent) game.getPermanentOrLKIBattlefield(source.getSourceId()); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null + && aura != null + && aura.getAttachedTo() != null) { + Permanent enchantedCreature = game.getPermanent(aura.getAttachedTo()); + controller.moveCards(aura, Zone.HAND, source, game); + if (enchantedCreature != null) { + controller.moveCards(enchantedCreature, Zone.HAND, source, game); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/f/Fatigue.java b/Mage.Sets/src/mage/cards/f/Fatigue.java new file mode 100644 index 00000000000..8f3ae9784b2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/Fatigue.java @@ -0,0 +1,36 @@ +package mage.cards.f; + +import java.util.UUID; +import mage.abilities.effects.common.SkipNextDrawStepTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.TargetPlayer; + +/** + * + * @author jeffwadsworth + */ +public final class Fatigue extends CardImpl { + + private static final String rule = "Target player skips his or her next draw step."; + + public Fatigue(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}"); + + + // Target player skips his or her next draw step. + this.getSpellAbility().addEffect(new SkipNextDrawStepTargetEffect().setText(rule)); + this.getSpellAbility().addTarget(new TargetPlayer()); + + } + + public Fatigue(final Fatigue card) { + super(card); + } + + @Override + public Fatigue copy() { + return new Fatigue(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/SkipNextDrawStepTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SkipNextDrawStepTargetEffect.java new file mode 100644 index 00000000000..2f1ba7d187e --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/SkipNextDrawStepTargetEffect.java @@ -0,0 +1,55 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author jeffwadsworth + */ + +public class SkipNextDrawStepTargetEffect extends ReplacementEffectImpl { + + public SkipNextDrawStepTargetEffect() { + super(Duration.OneUse, Outcome.Detriment); + staticText = "Target player skips his or her next draw step"; + } + + public SkipNextDrawStepTargetEffect(final SkipNextDrawStepTargetEffect effect) { + super(effect); + } + + @Override + public SkipNextDrawStepTargetEffect copy() { + return new SkipNextDrawStepTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DRAW_STEP; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getPlayerId().equals(source.getFirstTarget()); + } +} \ No newline at end of file From 2e9879616c0c9e2282758509eeab78aa72c2d2dd Mon Sep 17 00:00:00 2001 From: jeffwadsworth Date: Sun, 25 Nov 2018 10:52:16 -0600 Subject: [PATCH 43/61] - Updated Urza's Destiny set. --- Mage.Sets/src/mage/sets/UrzasDestiny.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/sets/UrzasDestiny.java b/Mage.Sets/src/mage/sets/UrzasDestiny.java index 2590ab67e58..d5b344ce21b 100644 --- a/Mage.Sets/src/mage/sets/UrzasDestiny.java +++ b/Mage.Sets/src/mage/sets/UrzasDestiny.java @@ -54,6 +54,7 @@ public final class UrzasDestiny extends ExpansionSet { cards.add(new SetCardInfo("Colos Yearling", 79, Rarity.COMMON, mage.cards.c.ColosYearling.class)); cards.add(new SetCardInfo("Compost", 102, Rarity.UNCOMMON, mage.cards.c.Compost.class)); cards.add(new SetCardInfo("Covetous Dragon", 80, Rarity.RARE, mage.cards.c.CovetousDragon.class)); + cards.add(new SetCardInfo("Disappear", 30, Rarity.UNCOMMON, mage.cards.d.Disappear.class)); cards.add(new SetCardInfo("Disease Carriers", 57, Rarity.COMMON, mage.cards.d.DiseaseCarriers.class)); cards.add(new SetCardInfo("Donate", 31, Rarity.RARE, mage.cards.d.Donate.class)); cards.add(new SetCardInfo("Dying Wail", 58, Rarity.COMMON, mage.cards.d.DyingWail.class)); @@ -64,6 +65,7 @@ public final class UrzasDestiny extends ExpansionSet { cards.add(new SetCardInfo("Eradicate", 60, Rarity.UNCOMMON, mage.cards.e.Eradicate.class)); cards.add(new SetCardInfo("Extruder", 130, Rarity.UNCOMMON, mage.cards.e.Extruder.class)); cards.add(new SetCardInfo("False Prophet", 6, Rarity.RARE, mage.cards.f.FalseProphet.class)); + cards.add(new SetCardInfo("Fatigue", 32, Rarity.COMMON, mage.cards.f.Fatigue.class)); cards.add(new SetCardInfo("Fend Off", 7, Rarity.COMMON, mage.cards.f.FendOff.class)); cards.add(new SetCardInfo("Festering Wound", 61, Rarity.UNCOMMON, mage.cards.f.FesteringWound.class)); cards.add(new SetCardInfo("Field Surgeon", 8, Rarity.COMMON, mage.cards.f.FieldSurgeon.class)); From 4f0725586b5d7ffbb7edb2a4fbc2cfc5eed4c104 Mon Sep 17 00:00:00 2001 From: jeffwadsworth Date: Sun, 25 Nov 2018 11:40:10 -0600 Subject: [PATCH 44/61] - Urza's Destiny 100%. Added Private Research, Lurking Jackals, Incendiary. --- Mage.Sets/src/mage/cards/i/Incendiary.java | 65 +++++++++ .../src/mage/cards/l/LurkingJackals.java | 123 ++++++++++++++++++ .../src/mage/cards/p/PrivateResearch.java | 60 +++++++++ Mage.Sets/src/mage/sets/UrzasDestiny.java | 3 + 4 files changed, 251 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/i/Incendiary.java create mode 100644 Mage.Sets/src/mage/cards/l/LurkingJackals.java create mode 100644 Mage.Sets/src/mage/cards/p/PrivateResearch.java diff --git a/Mage.Sets/src/mage/cards/i/Incendiary.java b/Mage.Sets/src/mage/cards/i/Incendiary.java new file mode 100644 index 00000000000..f5987a4793c --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/Incendiary.java @@ -0,0 +1,65 @@ +package mage.cards.i; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DiesAttachedTriggeredAbility; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.target.common.TargetCreatureOrPlayer; + +/** + * + * @author jeffwadsworth + */ +public final class Incendiary extends CardImpl { + + private static final String rule = "{this} deals X damage to any target, where X is the number of fuse counters on {this}."; + + public Incendiary(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // At the beginning of your upkeep, you may put a fuse counter on Incendiary. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.FUSE.createInstance(), true), TargetController.YOU, true)); + + // When enchanted creature dies, Incendiary deals X damage to any target, where X is the number of fuse counters on Incendiary. + Effect effect = new DamageTargetEffect(new CountersSourceCount(CounterType.FUSE)).setText(rule); + Ability ability2 = new DiesAttachedTriggeredAbility(effect, "enchanted creature"); + ability.addTarget(new TargetCreatureOrPlayer()); + this.addAbility(ability2); + + } + + public Incendiary(final Incendiary card) { + super(card); + } + + @Override + public Incendiary copy() { + return new Incendiary(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LurkingJackals.java b/Mage.Sets/src/mage/cards/l/LurkingJackals.java new file mode 100644 index 00000000000..2ff584e5a7e --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LurkingJackals.java @@ -0,0 +1,123 @@ +package mage.cards.l; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.StateTriggeredAbility; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.TokenImpl; + +/** + * + * @author jeffwadsworth + */ +public final class LurkingJackals extends CardImpl { + + public LurkingJackals(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}"); + + // When an opponent has 10 or less life, if Lurking Jackals is an enchantment, it becomes a 3/2 Hound creature. + this.addAbility(new LurkingJackalsStateTriggeredAbility()); + } + + public LurkingJackals(final LurkingJackals card) { + super(card); + } + + @Override + public LurkingJackals copy() { + return new LurkingJackals(this); + } +} + +class LurkingJackalsStateTriggeredAbility extends StateTriggeredAbility { + + public LurkingJackalsStateTriggeredAbility() { + super(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new LurkingJackalsToken(), "", Duration.Custom, true, false)); + } + + public LurkingJackalsStateTriggeredAbility(final LurkingJackalsStateTriggeredAbility ability) { + super(ability); + } + + @Override + public LurkingJackalsStateTriggeredAbility copy() { + return new LurkingJackalsStateTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (game.getOpponents(getControllerId()).stream().anyMatch((playerId) -> (game.getPlayer(playerId).getLife() <= 10))) { + return true; + } + return false; + } + + @Override + public boolean checkInterveningIfClause(Game game) { + return this.getSourcePermanentIfItStillExists(game).getCardType().contains(CardType.ENCHANTMENT); + } + + @Override + public boolean canTrigger(Game game) { + //20100716 - 603.8 + Boolean triggered = (Boolean) game.getState().getValue(getSourceId().toString() + "triggered"); + if (triggered == null) { + triggered = Boolean.FALSE; + } + return !triggered; + } + + @Override + public void trigger(Game game, UUID controllerId) { + //20100716 - 603.8 + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); + super.trigger(game, controllerId); + } + + @Override + public boolean resolve(Game game) { + //20100716 - 603.8 + boolean result = super.resolve(game); + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); + return result; + } + + @Override + public void counter(Game game) { + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); + } + + @Override + public String getRule() { + return new StringBuilder("When an opponent has 10 or less life, if {this} is an enchantment, ").append(super.getRule()).toString(); + } + +} + +class LurkingJackalsToken extends TokenImpl { + + public LurkingJackalsToken() { + super("Hound", "3/2 Hound creature"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.HOUND); + power = new MageInt(3); + toughness = new MageInt(2); + } + + public LurkingJackalsToken(final LurkingJackalsToken token) { + super(token); + } + + @Override + public LurkingJackalsToken copy() { + return new LurkingJackalsToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PrivateResearch.java b/Mage.Sets/src/mage/cards/p/PrivateResearch.java new file mode 100644 index 00000000000..bef10ca1c29 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PrivateResearch.java @@ -0,0 +1,60 @@ +package mage.cards.p; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DiesAttachedTriggeredAbility; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.counters.CounterType; + +/** + * + * @author jeffwadsworth + */ +public final class PrivateResearch extends CardImpl { + + private static final String rule = "draw a card for each page counter on {this}."; + + public PrivateResearch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // At the beginning of your upkeep, you may put a page counter on Private Research. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.PAGE.createInstance(), true), TargetController.YOU, true)); + + // When enchanted creature dies, draw a card for each page counter on Private Research. + this.addAbility(new DiesAttachedTriggeredAbility(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.PAGE)).setText(rule), "enchanted creature")); + + } + + public PrivateResearch(final PrivateResearch card) { + super(card); + } + + @Override + public PrivateResearch copy() { + return new PrivateResearch(this); + } +} diff --git a/Mage.Sets/src/mage/sets/UrzasDestiny.java b/Mage.Sets/src/mage/sets/UrzasDestiny.java index d5b344ce21b..aae8ca309c2 100644 --- a/Mage.Sets/src/mage/sets/UrzasDestiny.java +++ b/Mage.Sets/src/mage/sets/UrzasDestiny.java @@ -85,6 +85,7 @@ public final class UrzasDestiny extends ExpansionSet { cards.add(new SetCardInfo("Hunting Moa", 109, Rarity.UNCOMMON, mage.cards.h.HuntingMoa.class)); cards.add(new SetCardInfo("Illuminated Wings", 34, Rarity.COMMON, mage.cards.i.IlluminatedWings.class)); cards.add(new SetCardInfo("Impatience", 88, Rarity.RARE, mage.cards.i.Impatience.class)); + cards.add(new SetCardInfo("Incendiary", 89, Rarity.UNCOMMON, mage.cards.i.Incendiary.class)); cards.add(new SetCardInfo("Iridescent Drake", 35, Rarity.UNCOMMON, mage.cards.i.IridescentDrake.class)); cards.add(new SetCardInfo("Ivy Seer", 110, Rarity.UNCOMMON, mage.cards.i.IvySeer.class)); cards.add(new SetCardInfo("Jasmine Seer", 10, Rarity.UNCOMMON, mage.cards.j.JasmineSeer.class)); @@ -93,6 +94,7 @@ public final class UrzasDestiny extends ExpansionSet { cards.add(new SetCardInfo("Keldon Vandals", 91, Rarity.COMMON, mage.cards.k.KeldonVandals.class)); cards.add(new SetCardInfo("Kingfisher", 36, Rarity.COMMON, mage.cards.k.Kingfisher.class)); cards.add(new SetCardInfo("Landslide", 92, Rarity.UNCOMMON, mage.cards.l.Landslide.class)); + cards.add(new SetCardInfo("Lurking Jackals", 62, Rarity.UNCOMMON, mage.cards.l.LurkingJackals.class)); cards.add(new SetCardInfo("Magnify", 111, Rarity.COMMON, mage.cards.m.Magnify.class)); cards.add(new SetCardInfo("Mantis Engine", 133, Rarity.UNCOMMON, mage.cards.m.MantisEngine.class)); cards.add(new SetCardInfo("Marker Beetles", 112, Rarity.COMMON, mage.cards.m.MarkerBeetles.class)); @@ -116,6 +118,7 @@ public final class UrzasDestiny extends ExpansionSet { cards.add(new SetCardInfo("Plated Spider", 116, Rarity.COMMON, mage.cards.p.PlatedSpider.class)); cards.add(new SetCardInfo("Plow Under", 117, Rarity.RARE, mage.cards.p.PlowUnder.class)); cards.add(new SetCardInfo("Powder Keg", 136, Rarity.RARE, mage.cards.p.PowderKeg.class)); + cards.add(new SetCardInfo("Private Research", 41, Rarity.UNCOMMON, mage.cards.p.PrivateResearch.class)); cards.add(new SetCardInfo("Quash", 42, Rarity.UNCOMMON, mage.cards.q.Quash.class)); cards.add(new SetCardInfo("Rapid Decay", 67, Rarity.RARE, mage.cards.r.RapidDecay.class)); cards.add(new SetCardInfo("Ravenous Rats", 68, Rarity.COMMON, mage.cards.r.RavenousRats.class)); From a01891f11e3578ba05c273f88e5e40a03d3c9866 Mon Sep 17 00:00:00 2001 From: jeffwadsworth Date: Sun, 25 Nov 2018 19:11:12 -0600 Subject: [PATCH 45/61] - added some null checks. --- Mage.Sets/src/mage/cards/h/HiddenPredators.java | 5 ++++- Mage.Sets/src/mage/cards/l/LurkingJackals.java | 13 ++++++++++--- Mage.Sets/src/mage/cards/o/OpalAvenger.java | 10 ++++++++-- Mage.Sets/src/mage/cards/v/VeiledCrocodile.java | 5 ++++- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/cards/h/HiddenPredators.java b/Mage.Sets/src/mage/cards/h/HiddenPredators.java index f5ff50d09b3..88f532926e3 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenPredators.java +++ b/Mage.Sets/src/mage/cards/h/HiddenPredators.java @@ -71,7 +71,10 @@ class HiddenPredatorsStateTriggeredAbility extends StateTriggeredAbility { @Override public boolean checkInterveningIfClause(Game game) { - return this.getSourcePermanentIfItStillExists(game).getCardType().contains(CardType.ENCHANTMENT); + if (getSourcePermanentIfItStillExists(game) != null) { + return getSourcePermanentIfItStillExists(game).isEnchantment(); + } + return false; } @Override diff --git a/Mage.Sets/src/mage/cards/l/LurkingJackals.java b/Mage.Sets/src/mage/cards/l/LurkingJackals.java index 2ff584e5a7e..4822bb2c2de 100644 --- a/Mage.Sets/src/mage/cards/l/LurkingJackals.java +++ b/Mage.Sets/src/mage/cards/l/LurkingJackals.java @@ -54,15 +54,22 @@ class LurkingJackalsStateTriggeredAbility extends StateTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (game.getOpponents(getControllerId()).stream().anyMatch((playerId) -> (game.getPlayer(playerId).getLife() <= 10))) { - return true; + if (game.getOpponents(getControllerId()) != null) { + for (UUID opponentId : game.getOpponents(getControllerId())) { + if (game.getPlayer(opponentId).getLife() <= 10) { + return true; + } + } } return false; } @Override public boolean checkInterveningIfClause(Game game) { - return this.getSourcePermanentIfItStillExists(game).getCardType().contains(CardType.ENCHANTMENT); + if (getSourcePermanentIfItStillExists(game) != null) { + return getSourcePermanentIfItStillExists(game).isEnchantment(); + } + return false; } @Override diff --git a/Mage.Sets/src/mage/cards/o/OpalAvenger.java b/Mage.Sets/src/mage/cards/o/OpalAvenger.java index 5586cbedf6e..cc28e074245 100644 --- a/Mage.Sets/src/mage/cards/o/OpalAvenger.java +++ b/Mage.Sets/src/mage/cards/o/OpalAvenger.java @@ -54,12 +54,18 @@ class OpalAvengerStateTriggeredAbility extends StateTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { - return game.getState().getPlayer(getControllerId()).getLife() <= 10; + if (game.getState().getPlayer(getControllerId()) != null) { + return game.getState().getPlayer(getControllerId()).getLife() <= 10; + } + return false; } @Override public boolean checkInterveningIfClause(Game game) { - return this.getSourcePermanentIfItStillExists(game).getCardType().contains(CardType.ENCHANTMENT); + if (getSourcePermanentIfItStillExists(game) != null) { + return getSourcePermanentIfItStillExists(game).isEnchantment(); + } + return false; } @Override diff --git a/Mage.Sets/src/mage/cards/v/VeiledCrocodile.java b/Mage.Sets/src/mage/cards/v/VeiledCrocodile.java index c3790e5c738..860795598af 100644 --- a/Mage.Sets/src/mage/cards/v/VeiledCrocodile.java +++ b/Mage.Sets/src/mage/cards/v/VeiledCrocodile.java @@ -67,7 +67,10 @@ class VeiledCrocodileStateTriggeredAbility extends StateTriggeredAbility { @Override public boolean checkInterveningIfClause(Game game) { - return this.getSourcePermanentIfItStillExists(game).getCardType().contains(CardType.ENCHANTMENT); + if (getSourcePermanentIfItStillExists(game) != null) { + return getSourcePermanentIfItStillExists(game).isEnchantment(); + } + return false; } @Override From 4f1e9b0394380acae4f324e53407cc2457c390b6 Mon Sep 17 00:00:00 2001 From: L_J Date: Mon, 26 Nov 2018 04:16:41 +0100 Subject: [PATCH 46/61] Implemented some Legends cards --- Mage.Sets/src/mage/cards/b/BronzeHorse.java | 91 +++++++++++++++ Mage.Sets/src/mage/cards/e/ElderSpawn.java | 102 +++++++++++++++++ .../src/mage/cards/e/EnchantedBeing.java | 79 +++++++++++++ .../src/mage/cards/f/ForethoughtAmulet.java | 91 +++++++++++++++ Mage.Sets/src/mage/cards/k/KryShield.java | 49 ++++++++ Mage.Sets/src/mage/cards/r/Reverberation.java | 105 ++++++++++++++++++ Mage.Sets/src/mage/cards/s/Subdue.java | 39 +++++++ .../src/mage/cards/w/WallOfTombstones.java | 77 +++++++++++++ Mage.Sets/src/mage/sets/Chronicles.java | 1 + Mage.Sets/src/mage/sets/Legends.java | 16 +++ 10 files changed, 650 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/b/BronzeHorse.java create mode 100644 Mage.Sets/src/mage/cards/e/ElderSpawn.java create mode 100644 Mage.Sets/src/mage/cards/e/EnchantedBeing.java create mode 100644 Mage.Sets/src/mage/cards/f/ForethoughtAmulet.java create mode 100644 Mage.Sets/src/mage/cards/k/KryShield.java create mode 100644 Mage.Sets/src/mage/cards/r/Reverberation.java create mode 100644 Mage.Sets/src/mage/cards/s/Subdue.java create mode 100644 Mage.Sets/src/mage/cards/w/WallOfTombstones.java diff --git a/Mage.Sets/src/mage/cards/b/BronzeHorse.java b/Mage.Sets/src/mage/cards/b/BronzeHorse.java new file mode 100644 index 00000000000..28180cf2bb4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BronzeHorse.java @@ -0,0 +1,91 @@ + +package mage.cards.b; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalReplacementEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PreventAllDamageToSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.target.Target; + +/** + * + * @author L_J + */ +public final class BronzeHorse extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(new AnotherPredicate()); + } + + public BronzeHorse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT, CardType.CREATURE},"{7}"); + this.subtype.add(SubType.HORSE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // As long as you control another creature, prevent all damage that would be dealt to Bronze Horse by spells that target it. + Effect effect = new ConditionalReplacementEffect(new PreventDamageToSourceBySpellsThatTargetIt(), new PermanentsOnTheBattlefieldCondition(filter)); + effect.setText("As long as you control another creature, prevent all damage that would be dealt to {this} by spells that target it."); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + } + + public BronzeHorse(final BronzeHorse card) { + super(card); + } + + @Override + public BronzeHorse copy() { + return new BronzeHorse(this); + } +} + +class PreventDamageToSourceBySpellsThatTargetIt extends PreventAllDamageToSourceEffect { + + public PreventDamageToSourceBySpellsThatTargetIt() { + super(Duration.WhileOnBattlefield); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game)) { + if (event.getTargetId().equals(source.getSourceId())) { + Spell spell = game.getStack().getSpell(event.getSourceId()); + if (spell != null) { + for (UUID modeId : spell.getStackAbility().getModes().getSelectedModes()) { + Mode mode = spell.getStackAbility().getModes().get(modeId); + for (Target target : mode.getTargets()) { + for (UUID targetId : target.getTargets()) { + if (targetId.equals(source.getSourceId())) { + return true; + } + } + } + } + } + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/e/ElderSpawn.java b/Mage.Sets/src/mage/cards/e/ElderSpawn.java new file mode 100644 index 00000000000..1d65526cdcb --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ElderSpawn.java @@ -0,0 +1,102 @@ + +package mage.cards.e; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.SimpleEvasionAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; +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.TargetController; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author L_J + */ +public final class ElderSpawn extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("red creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + } + + public ElderSpawn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}{U}"); + this.subtype.add(SubType.SPAWN); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // At the beginning of your upkeep, unless you sacrifice an Island, sacrifice Elder Spawn and it deals 6 damage to you. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ElderSpawnEffect(), TargetController.YOU, false)); + + // Elder Spawn can't be blocked by red creatures. + this.addAbility(new SimpleEvasionAbility(new CantBeBlockedByCreaturesSourceEffect(filter, Duration.WhileOnBattlefield))); + } + + public ElderSpawn(final ElderSpawn card) { + super(card); + } + + @Override + public ElderSpawn copy() { + return new ElderSpawn(this); + } +} + +class ElderSpawnEffect extends OneShotEffect { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("an Island"); + static { + filter.add(new SubtypePredicate(SubType.ISLAND)); + } + + public ElderSpawnEffect() { + super(Outcome.Sacrifice); + staticText = "unless you sacrifice an Island, sacrifice {this} and it deals 6 damage to you"; + } + + public ElderSpawnEffect(final ElderSpawnEffect effect) { + super(effect); + } + + @Override + public ElderSpawnEffect copy() { + return new ElderSpawnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (controller != null && sourcePermanent != null) { + TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, true); + SacrificeTargetCost cost = new SacrificeTargetCost(target); + if (!controller.chooseUse(Outcome.AIDontUseIt, "Do you wish to sacrifice an Island?", source, game) + || !cost.canPay(source, source.getSourceId(), source.getControllerId(), game) + || !cost.pay(source, game, source.getSourceId(), source.getControllerId(), true)) { + sourcePermanent.sacrifice(source.getSourceId(), game); + controller.damage(6, sourcePermanent.getId(), game, false, true); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EnchantedBeing.java b/Mage.Sets/src/mage/cards/e/EnchantedBeing.java new file mode 100644 index 00000000000..0843ff51f8a --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EnchantedBeing.java @@ -0,0 +1,79 @@ + +package mage.cards.e; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PreventAllDamageToSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * + * @author L_J + */ +public final class EnchantedBeing extends CardImpl { + + public EnchantedBeing(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}"); + this.subtype.add(SubType.HUMAN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Prevent all damage that would be dealt to Enchanted Being by enchanted creatures. + Effect effect = new PreventDamageToSourceByEnchantedCreatures(); + effect.setText("Prevent all damage that would be dealt to {this} by enchanted creatures."); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + } + + public EnchantedBeing(final EnchantedBeing card) { + super(card); + } + + @Override + public EnchantedBeing copy() { + return new EnchantedBeing(this); + } +} + +class PreventDamageToSourceByEnchantedCreatures extends PreventAllDamageToSourceEffect { + + public PreventDamageToSourceByEnchantedCreatures() { + super(Duration.WhileOnBattlefield); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game)) { + if (isEnchantedCreature(game.getObject(event.getSourceId()), game)) { + if (event.getTargetId().equals(source.getSourceId())) { + return true; + } + } + } + return false; + } + + public boolean isEnchantedCreature(MageObject input, Game game) { + if (input == null || input.isCreature()) { + return false; + } + for (UUID attachmentId : ((Permanent) input).getAttachments()) { + Permanent attachment = game.getPermanent(attachmentId); + if (attachment != null && attachment.isEnchantment()) { + return true; + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/f/ForethoughtAmulet.java b/Mage.Sets/src/mage/cards/f/ForethoughtAmulet.java new file mode 100644 index 00000000000..f6eff9d8ff1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/ForethoughtAmulet.java @@ -0,0 +1,91 @@ + +package mage.cards.f; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent; + +/** + * + * @author L_J + */ +public final class ForethoughtAmulet extends CardImpl { + + public ForethoughtAmulet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); + + // At the beginning of your upkeep, sacrifice Forethought Amulet unless you pay {3}. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new GenericManaCost(3)), TargetController.YOU, false)); + + // If an instant or sorcery source would deal 3 or more damage to you, it deals 2 damage to you instead. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ForethoughtAmuletEffect())); + } + + public ForethoughtAmulet(final ForethoughtAmulet card) { + super(card); + } + + @Override + public ForethoughtAmulet copy() { + return new ForethoughtAmulet(this); + } +} + +class ForethoughtAmuletEffect extends ReplacementEffectImpl { + + public ForethoughtAmuletEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral); + staticText = "If an instant or sorcery source would deal 3 or more damage to you, it deals 2 damage to you instead"; + } + + public ForethoughtAmuletEffect(final ForethoughtAmuletEffect effect) { + super(effect); + } + + @Override + public ForethoughtAmuletEffect copy() { + return new ForethoughtAmuletEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getAmount() >= 3) { + MageObject object = game.getObject(event.getSourceId()); + return object != null && (object.isInstant() || object.isSorcery()); + } + return false; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + if (event.getTargetId().equals(source.getControllerId())) { + event.setAmount(2); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KryShield.java b/Mage.Sets/src/mage/cards/k/KryShield.java new file mode 100644 index 00000000000..8fc55fc5dc4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KryShield.java @@ -0,0 +1,49 @@ + +package mage.cards.k; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.PreventDamageByTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.target.common.TargetControlledCreaturePermanent; + +/** + * + * @author L_J + */ +public final class KryShield extends CardImpl { + + public KryShield(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + + // {2}, {T}: Prevent all damage that would be dealt this turn by target creature you control. That creature gets +0/+X until end of turn, where X is its converted mana cost. + Effect effect = new PreventDamageByTargetEffect(Duration.EndOfTurn); + effect.setText("Prevent all damage that would be dealt this turn by target creature you control"); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new GenericManaCost(2)); + ability.addEffect(new BoostTargetEffect(new StaticValue(0), new TargetConvertedManaCost(), Duration.EndOfTurn, true) + .setText("That creature gets +0/+X until end of turn, where X is its converted mana cost")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + public KryShield(final KryShield card) { + super(card); + } + + @Override + public KryShield copy() { + return new KryShield(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/Reverberation.java b/Mage.Sets/src/mage/cards/r/Reverberation.java new file mode 100644 index 00000000000..0a0768ae700 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/Reverberation.java @@ -0,0 +1,105 @@ + +package mage.cards.r; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.game.Game; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.TargetSpell; + +/** + * + * @author L_J + */ +public final class Reverberation extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("sorcery spell"); + + static { + filter.add(new CardTypePredicate(CardType.SORCERY)); + } + + public Reverberation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{U}"); + + // All damage that would be dealt this turn by target sorcery spell is dealt to that spell’s controller instead. + this.getSpellAbility().addEffect(new ReverberationEffect()); + this.getSpellAbility().addTarget(new TargetSpell(filter)); + } + + public Reverberation(final Reverberation card) { + super(card); + } + + @Override + public Reverberation copy() { + return new Reverberation(this); + } +} + +class ReverberationEffect extends ReplacementEffectImpl { + + public ReverberationEffect() { + super(Duration.EndOfTurn, Outcome.RedirectDamage); + staticText = "All damage that would be dealt this turn by target sorcery spell is dealt to that spell’s controller instead"; + } + + public ReverberationEffect(final ReverberationEffect effect) { + super(effect); + } + + @Override + public ReverberationEffect copy() { + return new ReverberationEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGE_CREATURE || + event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER || + event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + DamageEvent damageEvent = (DamageEvent) event; + if (controller != null) { + Spell targetSpell = game.getStack().getSpell(source.getFirstTarget()); + if (targetSpell != null) { + Player targetsController = game.getPlayer(targetSpell.getControllerId()); + if (targetsController != null) { + targetsController.damage(damageEvent.getAmount(), damageEvent.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects()); + return true; + } + } + } + return false; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + DamageEvent damageEvent = (DamageEvent) event; + Spell targetSpell = game.getStack().getSpell(source.getFirstTarget()); + if (targetSpell != null) { + return damageEvent.getAmount() > 0; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/Subdue.java b/Mage.Sets/src/mage/cards/s/Subdue.java new file mode 100644 index 00000000000..63ac77351e7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Subdue.java @@ -0,0 +1,39 @@ + +package mage.cards.s; + +import java.util.UUID; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.dynamicvalue.common.TargetConvertedManaCost; +import mage.abilities.effects.common.PreventDamageByTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author L_J + */ +public final class Subdue extends CardImpl { + + public Subdue(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); + + // Prevent all combat damage that would be dealt by target creature this turn. That creature gets +0/+X until end of turn, where X is its converted mana cost. + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new PreventDamageByTargetEffect(Duration.EndOfTurn, true)); + this.getSpellAbility().addEffect(new BoostTargetEffect(new StaticValue(0), new TargetConvertedManaCost(), Duration.EndOfTurn, true) + .setText("That creature gets +0/+X until end of turn, where X is its converted mana cost")); + } + + public Subdue(final Subdue card) { + super(card); + } + + @Override + public Subdue copy() { + return new Subdue(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WallOfTombstones.java b/Mage.Sets/src/mage/cards/w/WallOfTombstones.java new file mode 100644 index 00000000000..16abfc19f7c --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WallOfTombstones.java @@ -0,0 +1,77 @@ + +package mage.cards.w; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.SetToughnessSourceEffect; +import mage.abilities.keyword.DefenderAbility; +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.SubLayer; +import mage.constants.TargetController; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.util.CardUtil; + +/** + * + * @author L_J + */ +public final class WallOfTombstones extends CardImpl { + + public WallOfTombstones(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); + this.subtype.add(SubType.WALL); + this.power = new MageInt(0); + this.toughness = new MageInt(1); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // At the beginning of your upkeep, change Wall of Tombstones’s base toughness to 1 plus the number of creature cards in your graveyard. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new WallOfTombstonesEffect(), TargetController.YOU, false)); + + } + + public WallOfTombstones(final WallOfTombstones card) { + super(card); + } + + @Override + public WallOfTombstones copy() { + return new WallOfTombstones(this); + } +} + +class WallOfTombstonesEffect extends OneShotEffect { + + public WallOfTombstonesEffect() { + super(Outcome.Detriment); + this.staticText = "change {this}’s base toughness to 1 plus the number of creature cards in your graveyard"; + } + + public WallOfTombstonesEffect(final WallOfTombstonesEffect effect) { + super(effect); + } + + @Override + public WallOfTombstonesEffect copy() { + return new WallOfTombstonesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int newToughness = CardUtil.addWithOverflowCheck(1, new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE).calculate(game, source, this)); + game.addEffect(new SetToughnessSourceEffect(new StaticValue(newToughness), Duration.Custom, SubLayer.SetPT_7b), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/Chronicles.java b/Mage.Sets/src/mage/sets/Chronicles.java index 050f42904b8..66a72016426 100644 --- a/Mage.Sets/src/mage/sets/Chronicles.java +++ b/Mage.Sets/src/mage/sets/Chronicles.java @@ -47,6 +47,7 @@ public final class Chronicles extends ExpansionSet { cards.add(new SetCardInfo("Bog Rats", 2, Rarity.COMMON, mage.cards.b.BogRats.class)); cards.add(new SetCardInfo("Book of Rass", 75, Rarity.RARE, mage.cards.b.BookOfRass.class)); cards.add(new SetCardInfo("Boomerang", 16, Rarity.COMMON, mage.cards.b.Boomerang.class)); + cards.add(new SetCardInfo("Bronze Horse", 76, Rarity.RARE, mage.cards.b.BronzeHorse.class)); cards.add(new SetCardInfo("Cat Warriors", 30, Rarity.COMMON, mage.cards.c.CatWarriors.class)); cards.add(new SetCardInfo("Chromium", 109, Rarity.RARE, mage.cards.c.Chromium.class)); cards.add(new SetCardInfo("City of Brass", 92, Rarity.RARE, mage.cards.c.CityOfBrass.class)); diff --git a/Mage.Sets/src/mage/sets/Legends.java b/Mage.Sets/src/mage/sets/Legends.java index 68a33d49747..134bb3b75cb 100644 --- a/Mage.Sets/src/mage/sets/Legends.java +++ b/Mage.Sets/src/mage/sets/Legends.java @@ -29,6 +29,7 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Acid Rain", 44, Rarity.RARE, mage.cards.a.AcidRain.class)); cards.add(new SetCardInfo("Active Volcano", 130, Rarity.COMMON, mage.cards.a.ActiveVolcano.class)); cards.add(new SetCardInfo("Adun Oakenshield", 216, Rarity.RARE, mage.cards.a.AdunOakenshield.class)); + cards.add(new SetCardInfo("Adventurers' Guildhouse", 300, Rarity.UNCOMMON, mage.cards.a.AdventurersGuildhouse.class)); cards.add(new SetCardInfo("Aerathi Berserker", 131, Rarity.UNCOMMON, mage.cards.a.AerathiBerserker.class)); cards.add(new SetCardInfo("Aisling Leprechaun", 173, Rarity.COMMON, mage.cards.a.AislingLeprechaun.class)); cards.add(new SetCardInfo("Akron Legionnaire", 1, Rarity.RARE, mage.cards.a.AkronLegionnaire.class)); @@ -58,8 +59,10 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Blue Mana Battery", 275, Rarity.UNCOMMON, mage.cards.b.BlueManaBattery.class)); cards.add(new SetCardInfo("Boomerang", 48, Rarity.COMMON, mage.cards.b.Boomerang.class)); cards.add(new SetCardInfo("Boris Devilboon", 223, Rarity.RARE, mage.cards.b.BorisDevilboon.class)); + cards.add(new SetCardInfo("Bronze Horse", 276, Rarity.RARE, mage.cards.b.BronzeHorse.class)); cards.add(new SetCardInfo("Carrion Ants", 90, Rarity.RARE, mage.cards.c.CarrionAnts.class)); cards.add(new SetCardInfo("Cat Warriors", 177, Rarity.COMMON, mage.cards.c.CatWarriors.class)); + cards.add(new SetCardInfo("Cathedral of Serra", 301, Rarity.UNCOMMON, mage.cards.c.CathedralOfSerra.class)); cards.add(new SetCardInfo("Caverns of Despair", 136, Rarity.RARE, mage.cards.c.CavernsOfDespair.class)); cards.add(new SetCardInfo("Chain Lightning", 137, Rarity.COMMON, mage.cards.c.ChainLightning.class)); cards.add(new SetCardInfo("Chains of Mephistopheles", 91, Rarity.RARE, mage.cards.c.ChainsOfMephistopheles.class)); @@ -88,8 +91,10 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Durkwood Boars", 182, Rarity.COMMON, mage.cards.d.DurkwoodBoars.class)); cards.add(new SetCardInfo("Dwarven Song", 143, Rarity.UNCOMMON, mage.cards.d.DwarvenSong.class)); cards.add(new SetCardInfo("Elder Land Wurm", 11, Rarity.RARE, mage.cards.e.ElderLandWurm.class)); + cards.add(new SetCardInfo("Elder Spawn", 52, Rarity.RARE, mage.cards.e.ElderSpawn.class)); cards.add(new SetCardInfo("Elven Riders", 183, Rarity.RARE, mage.cards.e.ElvenRiders.class)); cards.add(new SetCardInfo("Emerald Dragonfly", 184, Rarity.COMMON, mage.cards.e.EmeraldDragonfly.class)); + cards.add(new SetCardInfo("Enchanted Being", 12, Rarity.COMMON, mage.cards.e.EnchantedBeing.class)); cards.add(new SetCardInfo("Enchantment Alteration", 53, Rarity.COMMON, mage.cards.e.EnchantmentAlteration.class)); cards.add(new SetCardInfo("Energy Tap", 54, Rarity.COMMON, mage.cards.e.EnergyTap.class)); cards.add(new SetCardInfo("Eternal Warrior", 144, Rarity.UNCOMMON, mage.cards.e.EternalWarrior.class)); @@ -102,6 +107,7 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Flash Flood", 57, Rarity.COMMON, mage.cards.f.FlashFlood.class)); cards.add(new SetCardInfo("Floral Spuzzem", 187, Rarity.UNCOMMON, mage.cards.f.FloralSpuzzem.class)); cards.add(new SetCardInfo("Force Spike", 58, Rarity.COMMON, mage.cards.f.ForceSpike.class)); + cards.add(new SetCardInfo("Forethought Amulet", 277, Rarity.RARE, mage.cards.f.ForethoughtAmulet.class)); cards.add(new SetCardInfo("Fortified Area", 14, Rarity.UNCOMMON, mage.cards.f.FortifiedArea.class)); cards.add(new SetCardInfo("Frost Giant", 148, Rarity.UNCOMMON, mage.cards.f.FrostGiant.class)); cards.add(new SetCardInfo("Gabriel Angelfire", 226, Rarity.RARE, mage.cards.g.GabrielAngelfire.class)); @@ -159,6 +165,7 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Kobold Overlord", 155, Rarity.RARE, mage.cards.k.KoboldOverlord.class)); cards.add(new SetCardInfo("Kobold Taskmaster", 156, Rarity.UNCOMMON, mage.cards.k.KoboldTaskmaster.class)); cards.add(new SetCardInfo("Kobolds of Kher Keep", 157, Rarity.COMMON, mage.cards.k.KoboldsOfKherKeep.class)); + cards.add(new SetCardInfo("Kry Shield", 282, Rarity.UNCOMMON, mage.cards.k.KryShield.class)); cards.add(new SetCardInfo("Lady Caleria", 239, Rarity.RARE, mage.cards.l.LadyCaleria.class)); cards.add(new SetCardInfo("Lady Evangela", 240, Rarity.RARE, mage.cards.l.LadyEvangela.class)); cards.add(new SetCardInfo("Lady Orca", 241, Rarity.UNCOMMON, mage.cards.l.LadyOrca.class)); @@ -175,10 +182,12 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Mana Matrix", 285, Rarity.RARE, mage.cards.m.ManaMatrix.class)); cards.add(new SetCardInfo("Marble Priest", 286, Rarity.UNCOMMON, mage.cards.m.MarblePriest.class)); cards.add(new SetCardInfo("Marhault Elsdragon", 244, Rarity.UNCOMMON, mage.cards.m.MarhaultElsdragon.class)); + cards.add(new SetCardInfo("Master of the Hunt", 194, Rarity.RARE, mage.cards.m.MasterOfTheHunt.class)); cards.add(new SetCardInfo("Mirror Universe", 287, Rarity.RARE, mage.cards.m.MirrorUniverse.class)); cards.add(new SetCardInfo("Moat", 28, Rarity.RARE, mage.cards.m.Moat.class)); cards.add(new SetCardInfo("Mold Demon", 112, Rarity.RARE, mage.cards.m.MoldDemon.class)); cards.add(new SetCardInfo("Moss Monster", 195, Rarity.COMMON, mage.cards.m.MossMonster.class)); + cards.add(new SetCardInfo("Mountain Stronghold", 304, Rarity.UNCOMMON, mage.cards.m.MountainStronghold.class)); cards.add(new SetCardInfo("Mountain Yeti", 159, Rarity.UNCOMMON, mage.cards.m.MountainYeti.class)); cards.add(new SetCardInfo("Nebuchadnezzar", 245, Rarity.RARE, mage.cards.n.Nebuchadnezzar.class)); cards.add(new SetCardInfo("Nether Void", 113, Rarity.RARE, mage.cards.n.NetherVoid.class)); @@ -217,6 +226,7 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Remove Soul", 72, Rarity.COMMON, mage.cards.r.RemoveSoul.class)); cards.add(new SetCardInfo("Reset", 73, Rarity.UNCOMMON, mage.cards.r.Reset.class)); cards.add(new SetCardInfo("Revelation", 202, Rarity.RARE, mage.cards.r.Revelation.class)); + cards.add(new SetCardInfo("Reverberation", 74, Rarity.RARE, mage.cards.r.Reverberation.class)); cards.add(new SetCardInfo("Righteous Avengers", 34, Rarity.UNCOMMON, mage.cards.r.RighteousAvengers.class)); cards.add(new SetCardInfo("Ring of Immortals", 293, Rarity.RARE, mage.cards.r.RingOfImmortals.class)); cards.add(new SetCardInfo("Riven Turnbull", 254, Rarity.UNCOMMON, mage.cards.r.RivenTurnbull.class)); @@ -224,10 +234,12 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Rubinia Soulsinger", 256, Rarity.RARE, mage.cards.r.RubiniaSoulsinger.class)); cards.add(new SetCardInfo("Rust", 203, Rarity.COMMON, mage.cards.r.Rust.class)); cards.add(new SetCardInfo("Sea Kings' Blessing", 75, Rarity.UNCOMMON, mage.cards.s.SeaKingsBlessing.class)); + cards.add(new SetCardInfo("Seafarer's Quay", 306, Rarity.UNCOMMON, mage.cards.s.SeafarersQuay.class)); cards.add(new SetCardInfo("Seeker", 35, Rarity.UNCOMMON, mage.cards.s.Seeker.class)); cards.add(new SetCardInfo("Segovian Leviathan", 76, Rarity.UNCOMMON, mage.cards.s.SegovianLeviathan.class)); cards.add(new SetCardInfo("Sentinel", 294, Rarity.RARE, mage.cards.s.Sentinel.class)); cards.add(new SetCardInfo("Serpent Generator", 295, Rarity.RARE, mage.cards.s.SerpentGenerator.class)); + cards.add(new SetCardInfo("Shelkin Brownie", 204, Rarity.COMMON, mage.cards.s.ShelkinBrownie.class)); cards.add(new SetCardInfo("Shield Wall", 36, Rarity.UNCOMMON, mage.cards.s.ShieldWall.class)); cards.add(new SetCardInfo("Shimian Night Stalker", 116, Rarity.UNCOMMON, mage.cards.s.ShimianNightStalker.class)); cards.add(new SetCardInfo("Sir Shandlar of Eberyn", 257, Rarity.UNCOMMON, mage.cards.s.SirShandlarOfEberyn.class)); @@ -241,6 +253,7 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Stangg", 260, Rarity.RARE, mage.cards.s.Stangg.class)); cards.add(new SetCardInfo("Storm Seeker", 205, Rarity.UNCOMMON, mage.cards.s.StormSeeker.class)); cards.add(new SetCardInfo("Storm World", 165, Rarity.RARE, mage.cards.s.StormWorld.class)); + cards.add(new SetCardInfo("Subdue", 206, Rarity.COMMON, mage.cards.s.Subdue.class)); cards.add(new SetCardInfo("Sunastian Falconer", 261, Rarity.UNCOMMON, mage.cards.s.SunastianFalconer.class)); cards.add(new SetCardInfo("Sword of the Ages", 296, Rarity.RARE, mage.cards.s.SwordOfTheAges.class)); cards.add(new SetCardInfo("Sylvan Library", 207, Rarity.UNCOMMON, mage.cards.s.SylvanLibrary.class)); @@ -257,6 +270,7 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Thunder Spirit", 39, Rarity.RARE, mage.cards.t.ThunderSpirit.class)); cards.add(new SetCardInfo("Time Elemental", 81, Rarity.RARE, mage.cards.t.TimeElemental.class)); cards.add(new SetCardInfo("Tobias Andrion", 264, Rarity.UNCOMMON, mage.cards.t.TobiasAndrion.class)); + cards.add(new SetCardInfo("Tolaria", 308, Rarity.UNCOMMON, mage.cards.t.Tolaria.class)); cards.add(new SetCardInfo("Tor Wauki", 265, Rarity.UNCOMMON, mage.cards.t.TorWauki.class)); cards.add(new SetCardInfo("Torsten Von Ursus", 266, Rarity.UNCOMMON, mage.cards.t.TorstenVonUrsus.class)); cards.add(new SetCardInfo("Touch of Darkness", 122, Rarity.UNCOMMON, mage.cards.t.TouchOfDarkness.class)); @@ -266,6 +280,7 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Tundra Wolves", 40, Rarity.COMMON, mage.cards.t.TundraWolves.class)); cards.add(new SetCardInfo("Typhoon", 209, Rarity.RARE, mage.cards.t.Typhoon.class)); cards.add(new SetCardInfo("Undertow", 82, Rarity.UNCOMMON, mage.cards.u.Undertow.class)); + cards.add(new SetCardInfo("Unholy Citadel", 309, Rarity.UNCOMMON, mage.cards.u.UnholyCitadel.class)); cards.add(new SetCardInfo("Underworld Dreams", 124, Rarity.UNCOMMON, mage.cards.u.UnderworldDreams.class)); cards.add(new SetCardInfo("Untamed Wilds", 210, Rarity.UNCOMMON, mage.cards.u.UntamedWilds.class)); cards.add(new SetCardInfo("Ur-Drago", 268, Rarity.RARE, mage.cards.u.UrDrago.class)); @@ -281,6 +296,7 @@ public final class Legends extends ExpansionSet { cards.add(new SetCardInfo("Wall of Light", 43, Rarity.UNCOMMON, mage.cards.w.WallOfLight.class)); cards.add(new SetCardInfo("Wall of Opposition", 171, Rarity.RARE, mage.cards.w.WallOfOpposition.class)); cards.add(new SetCardInfo("Wall of Putrid Flesh", 127, Rarity.UNCOMMON, mage.cards.w.WallOfPutridFlesh.class)); + cards.add(new SetCardInfo("Wall of Tombstones", 129, Rarity.UNCOMMON, mage.cards.w.WallOfTombstones.class)); cards.add(new SetCardInfo("Wall of Vapor", 84, Rarity.COMMON, mage.cards.w.WallOfVapor.class)); cards.add(new SetCardInfo("Wall of Wonder", 85, Rarity.UNCOMMON, mage.cards.w.WallOfWonder.class)); cards.add(new SetCardInfo("Whirling Dervish", 211, Rarity.UNCOMMON, mage.cards.w.WhirlingDervish.class)); From 77ab63000150798a390f55107d1acf838f5e0cba Mon Sep 17 00:00:00 2001 From: jeffwadsworth Date: Sun, 25 Nov 2018 21:38:07 -0600 Subject: [PATCH 47/61] - Added Duplicity. --- Mage.Sets/src/mage/cards/d/Duplicity.java | 200 ++++++ Mage.Sets/src/mage/sets/Tempest.java | 737 +++++++++++----------- 2 files changed, 569 insertions(+), 368 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/d/Duplicity.java diff --git a/Mage.Sets/src/mage/cards/d/Duplicity.java b/Mage.Sets/src/mage/cards/d/Duplicity.java new file mode 100644 index 00000000000..ec2655933c0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/Duplicity.java @@ -0,0 +1,200 @@ +package mage.cards.d; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.discard.DiscardControllerEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author jeffwadsworth + */ +public final class Duplicity extends CardImpl { + + public Duplicity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); + + // When Duplicity enters the battlefield, exile the top five cards of your library face down. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DuplicityEffect(), false)); + + // At the beginning of your upkeep, you may exile all cards from your hand face down. If you do, put all other cards you own exiled with Duplicity into your hand. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DuplicityExileHandEffect(), TargetController.YOU, true)); + + // At the beginning of your end step, discard a card. + this.addAbility(new BeginningOfYourEndStepTriggeredAbility(new DiscardControllerEffect(1), false)); + + // When you lose control of Duplicity, put all cards exiled with Duplicity into their owner's graveyard. + this.addAbility(new LoseControlDuplicity()); + + } + + public Duplicity(final Duplicity card) { + super(card); + } + + @Override + public Duplicity copy() { + return new Duplicity(this); + } +} + +class DuplicityEffect extends OneShotEffect { + + public DuplicityEffect() { + super(Outcome.Exile); + staticText = "exile the top five cards of your library face down"; + } + + public DuplicityEffect(final DuplicityEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null + && sourceObject != null) { + if (controller.getLibrary().hasCards()) { + UUID exileId = CardUtil.getCardExileZoneId(game, source); + Set cardsToExile = controller.getLibrary().getTopCards(game, 5); + for (Card card : cardsToExile) { + controller.moveCardsToExile(card, source, game, true, exileId, sourceObject.getName()); + card.setFaceDown(true, game); + } + } + return true; + } + return false; + } + + @Override + public DuplicityEffect copy() { + return new DuplicityEffect(this); + } +} + +class DuplicityExileHandEffect extends OneShotEffect { + + public DuplicityExileHandEffect() { + super(Outcome.Exile); + staticText = "you may exile all cards from your hand face down. If you do, put all other cards you own exiled with {this} into your hand"; + } + + public DuplicityExileHandEffect(final DuplicityExileHandEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null + && sourceObject != null) { + if (!controller.getHand().isEmpty()) { + UUID exileId = CardUtil.getCardExileZoneId(game, source); + Set cardsFromHandToExile = controller.getHand().getCards(game); + for (Card card : cardsFromHandToExile) { + controller.moveCardsToExile(card, source, game, true, exileId, sourceObject.getName()); + card.setFaceDown(true, game); + } + Set cardsInExile = game.getExile().getExileZone(exileId).getCards(game); + Set cardsToReturnToHandFromExile = new HashSet<>(); + for (Card card : cardsInExile) { + if (!cardsFromHandToExile.contains(card)) { + cardsToReturnToHandFromExile.add(card); + } + } + controller.moveCards(cardsToReturnToHandFromExile, Zone.HAND, source, game); + } + return true; + } + return false; + } + + @Override + public DuplicityExileHandEffect copy() { + return new DuplicityExileHandEffect(this); + } +} + +class LoseControlDuplicity extends DelayedTriggeredAbility { + + public LoseControlDuplicity() { + super(new PutExiledCardsInOwnersGraveyard(), Duration.EndOfGame, false); + } + + public LoseControlDuplicity(final LoseControlDuplicity ability) { + super(ability); + } + + @Override + public LoseControlDuplicity copy() { + return new LoseControlDuplicity(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LOST_CONTROL; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getPlayerId().equals(controllerId); + } + + @Override + public String getRule() { + return "When you lose control of {this}, put all cards exiled with {this} into their owner's graveyard."; + } +} + +class PutExiledCardsInOwnersGraveyard extends OneShotEffect { + + public PutExiledCardsInOwnersGraveyard() { + super(Outcome.Neutral); + staticText = " put all cards exiled with {this} into their owner's graveyard."; + } + + public PutExiledCardsInOwnersGraveyard(final PutExiledCardsInOwnersGraveyard effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null + && sourceObject != null) { + UUID exileId = CardUtil.getCardExileZoneId(game, source); + Set cardsInExile = game.getExile().getExileZone(exileId).getCards(game); + controller.moveCardsToGraveyardWithInfo(cardsInExile, source, game, Zone.EXILED); + return true; + } + return false; + } + + @Override + public PutExiledCardsInOwnersGraveyard copy() { + return new PutExiledCardsInOwnersGraveyard(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Tempest.java b/Mage.Sets/src/mage/sets/Tempest.java index 5269f4c17c7..e1ec411d32e 100644 --- a/Mage.Sets/src/mage/sets/Tempest.java +++ b/Mage.Sets/src/mage/sets/Tempest.java @@ -1,368 +1,369 @@ -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -public final class Tempest extends ExpansionSet { - - private static final Tempest instance = new Tempest(); - - public static Tempest getInstance() { - return instance; - } - - private Tempest() { - super("Tempest", "TMP", ExpansionSet.buildDate(1997, 10, 1), SetType.EXPANSION); - this.blockName = "Tempest"; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Abandon Hope", 107, Rarity.UNCOMMON, mage.cards.a.AbandonHope.class)); - cards.add(new SetCardInfo("Advance Scout", 1, Rarity.COMMON, mage.cards.a.AdvanceScout.class)); - cards.add(new SetCardInfo("Aftershock", 160, Rarity.COMMON, mage.cards.a.Aftershock.class)); - cards.add(new SetCardInfo("Altar of Dementia", 276, Rarity.RARE, mage.cards.a.AltarOfDementia.class)); - cards.add(new SetCardInfo("Aluren", 213, Rarity.RARE, mage.cards.a.Aluren.class)); - cards.add(new SetCardInfo("Ancient Runes", 161, Rarity.UNCOMMON, mage.cards.a.AncientRunes.class)); - cards.add(new SetCardInfo("Ancient Tomb", 315, Rarity.UNCOMMON, mage.cards.a.AncientTomb.class)); - cards.add(new SetCardInfo("Angelic Protector", 2, Rarity.UNCOMMON, mage.cards.a.AngelicProtector.class)); - cards.add(new SetCardInfo("Anoint", 3, Rarity.COMMON, mage.cards.a.Anoint.class)); - cards.add(new SetCardInfo("Apes of Rath", 214, Rarity.UNCOMMON, mage.cards.a.ApesOfRath.class)); - cards.add(new SetCardInfo("Apocalypse", 162, Rarity.RARE, mage.cards.a.Apocalypse.class)); - cards.add(new SetCardInfo("Armor Sliver", 4, Rarity.UNCOMMON, mage.cards.a.ArmorSliver.class)); - cards.add(new SetCardInfo("Armored Pegasus", 5, Rarity.COMMON, mage.cards.a.ArmoredPegasus.class)); - cards.add(new SetCardInfo("Auratog", 6, Rarity.RARE, mage.cards.a.Auratog.class)); - cards.add(new SetCardInfo("Avenging Angel", 7, Rarity.RARE, mage.cards.a.AvengingAngel.class)); - cards.add(new SetCardInfo("Barbed Sliver", 163, Rarity.UNCOMMON, mage.cards.b.BarbedSliver.class)); - cards.add(new SetCardInfo("Bayou Dragonfly", 215, Rarity.COMMON, mage.cards.b.BayouDragonfly.class)); - cards.add(new SetCardInfo("Bellowing Fiend", 108, Rarity.RARE, mage.cards.b.BellowingFiend.class)); - cards.add(new SetCardInfo("Benthic Behemoth", 54, Rarity.RARE, mage.cards.b.BenthicBehemoth.class)); - cards.add(new SetCardInfo("Blood Frenzy", 164, Rarity.COMMON, mage.cards.b.BloodFrenzy.class)); - cards.add(new SetCardInfo("Blood Pet", 109, Rarity.COMMON, mage.cards.b.BloodPet.class)); - cards.add(new SetCardInfo("Boil", 165, Rarity.UNCOMMON, mage.cards.b.Boil.class)); - cards.add(new SetCardInfo("Booby Trap", 277, Rarity.RARE, mage.cards.b.BoobyTrap.class)); - cards.add(new SetCardInfo("Bottle Gnomes", 278, Rarity.UNCOMMON, mage.cards.b.BottleGnomes.class)); - cards.add(new SetCardInfo("Bounty Hunter", 110, Rarity.RARE, mage.cards.b.BountyHunter.class)); - cards.add(new SetCardInfo("Broken Fall", 216, Rarity.COMMON, mage.cards.b.BrokenFall.class)); - cards.add(new SetCardInfo("Caldera Lake", 316, Rarity.RARE, mage.cards.c.CalderaLake.class)); - cards.add(new SetCardInfo("Canopy Spider", 217, Rarity.COMMON, mage.cards.c.CanopySpider.class)); - cards.add(new SetCardInfo("Canyon Drake", 166, Rarity.RARE, mage.cards.c.CanyonDrake.class)); - cards.add(new SetCardInfo("Canyon Wildcat", 167, Rarity.COMMON, mage.cards.c.CanyonWildcat.class)); - cards.add(new SetCardInfo("Capsize", 55, Rarity.COMMON, mage.cards.c.Capsize.class)); - cards.add(new SetCardInfo("Carrionette", 111, Rarity.RARE, mage.cards.c.Carrionette.class)); - cards.add(new SetCardInfo("Chaotic Goo", 168, Rarity.RARE, mage.cards.c.ChaoticGoo.class)); - cards.add(new SetCardInfo("Charging Rhino", 218, Rarity.UNCOMMON, mage.cards.c.ChargingRhino.class)); - cards.add(new SetCardInfo("Chill", 56, Rarity.UNCOMMON, mage.cards.c.Chill.class)); - cards.add(new SetCardInfo("Choke", 219, Rarity.UNCOMMON, mage.cards.c.Choke.class)); - cards.add(new SetCardInfo("Cinder Marsh", 317, Rarity.UNCOMMON, mage.cards.c.CinderMarsh.class)); - cards.add(new SetCardInfo("Circle of Protection: Black", 8, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlack.class)); - cards.add(new SetCardInfo("Circle of Protection: Blue", 9, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlue.class)); - cards.add(new SetCardInfo("Circle of Protection: Green", 10, Rarity.COMMON, mage.cards.c.CircleOfProtectionGreen.class)); - cards.add(new SetCardInfo("Circle of Protection: Red", 11, Rarity.COMMON, mage.cards.c.CircleOfProtectionRed.class)); - cards.add(new SetCardInfo("Circle of Protection: Shadow", 12, Rarity.COMMON, mage.cards.c.CircleOfProtectionShadow.class)); - cards.add(new SetCardInfo("Circle of Protection: White", 13, Rarity.COMMON, mage.cards.c.CircleOfProtectionWhite.class)); - cards.add(new SetCardInfo("Clergy en-Vec", 14, Rarity.COMMON, mage.cards.c.ClergyEnVec.class)); - cards.add(new SetCardInfo("Clot Sliver", 112, Rarity.COMMON, mage.cards.c.ClotSliver.class)); - cards.add(new SetCardInfo("Cloudchaser Eagle", 15, Rarity.COMMON, mage.cards.c.CloudchaserEagle.class)); - cards.add(new SetCardInfo("Coercion", 113, Rarity.COMMON, mage.cards.c.Coercion.class)); - cards.add(new SetCardInfo("Coffin Queen", 114, Rarity.RARE, mage.cards.c.CoffinQueen.class)); - cards.add(new SetCardInfo("Coiled Tinviper", 279, Rarity.COMMON, mage.cards.c.CoiledTinviper.class)); - cards.add(new SetCardInfo("Cold Storage", 280, Rarity.RARE, mage.cards.c.ColdStorage.class)); - cards.add(new SetCardInfo("Commander Greven il-Vec", 115, Rarity.RARE, mage.cards.c.CommanderGrevenIlVec.class)); - cards.add(new SetCardInfo("Corpse Dance", 116, Rarity.RARE, mage.cards.c.CorpseDance.class)); - cards.add(new SetCardInfo("Counterspell", 57, Rarity.COMMON, mage.cards.c.Counterspell.class)); - cards.add(new SetCardInfo("Crazed Armodon", 220, Rarity.RARE, mage.cards.c.CrazedArmodon.class)); - cards.add(new SetCardInfo("Crown of Flames", 169, Rarity.COMMON, mage.cards.c.CrownOfFlames.class)); - cards.add(new SetCardInfo("Cursed Scroll", 281, Rarity.RARE, mage.cards.c.CursedScroll.class)); - cards.add(new SetCardInfo("Dark Banishing", 117, Rarity.COMMON, mage.cards.d.DarkBanishing.class)); - cards.add(new SetCardInfo("Dark Ritual", 118, Rarity.COMMON, mage.cards.d.DarkRitual.class)); - cards.add(new SetCardInfo("Darkling Stalker", 119, Rarity.COMMON, mage.cards.d.DarklingStalker.class)); - cards.add(new SetCardInfo("Dauthi Embrace", 120, Rarity.UNCOMMON, mage.cards.d.DauthiEmbrace.class)); - cards.add(new SetCardInfo("Dauthi Ghoul", 121, Rarity.UNCOMMON, mage.cards.d.DauthiGhoul.class)); - cards.add(new SetCardInfo("Dauthi Horror", 122, Rarity.COMMON, mage.cards.d.DauthiHorror.class)); - cards.add(new SetCardInfo("Dauthi Marauder", 123, Rarity.COMMON, mage.cards.d.DauthiMarauder.class)); - cards.add(new SetCardInfo("Dauthi Mercenary", 124, Rarity.UNCOMMON, mage.cards.d.DauthiMercenary.class)); - cards.add(new SetCardInfo("Dauthi Mindripper", 125, Rarity.UNCOMMON, mage.cards.d.DauthiMindripper.class)); - cards.add(new SetCardInfo("Dauthi Slayer", 126, Rarity.COMMON, mage.cards.d.DauthiSlayer.class)); - cards.add(new SetCardInfo("Deadshot", 170, Rarity.RARE, mage.cards.d.Deadshot.class)); - cards.add(new SetCardInfo("Death Pits of Rath", 127, Rarity.RARE, mage.cards.d.DeathPitsOfRath.class)); - cards.add(new SetCardInfo("Diabolic Edict", 128, Rarity.COMMON, mage.cards.d.DiabolicEdict.class)); - cards.add(new SetCardInfo("Dirtcowl Wurm", 221, Rarity.RARE, mage.cards.d.DirtcowlWurm.class)); - cards.add(new SetCardInfo("Disenchant", 16, Rarity.COMMON, mage.cards.d.Disenchant.class)); - cards.add(new SetCardInfo("Dismiss", 58, Rarity.UNCOMMON, mage.cards.d.Dismiss.class)); - cards.add(new SetCardInfo("Disturbed Burial", 129, Rarity.COMMON, mage.cards.d.DisturbedBurial.class)); - cards.add(new SetCardInfo("Dracoplasm", 266, Rarity.RARE, mage.cards.d.Dracoplasm.class)); - cards.add(new SetCardInfo("Dread of Night", 130, Rarity.UNCOMMON, mage.cards.d.DreadOfNight.class)); - cards.add(new SetCardInfo("Dream Cache", 59, Rarity.COMMON, mage.cards.d.DreamCache.class)); - cards.add(new SetCardInfo("Dregs of Sorrow", 131, Rarity.RARE, mage.cards.d.DregsOfSorrow.class)); - cards.add(new SetCardInfo("Earthcraft", 222, Rarity.RARE, mage.cards.e.Earthcraft.class)); - cards.add(new SetCardInfo("Echo Chamber", 282, Rarity.RARE, mage.cards.e.EchoChamber.class)); - cards.add(new SetCardInfo("Eladamri's Vineyard", 223, Rarity.RARE, mage.cards.e.EladamrisVineyard.class)); - cards.add(new SetCardInfo("Eladamri, Lord of Leaves", 224, Rarity.RARE, mage.cards.e.EladamriLordOfLeaves.class)); - cards.add(new SetCardInfo("Elite Javelineer", 17, Rarity.COMMON, mage.cards.e.EliteJavelineer.class)); - cards.add(new SetCardInfo("Elven Warhounds", 225, Rarity.RARE, mage.cards.e.ElvenWarhounds.class)); - cards.add(new SetCardInfo("Elvish Fury", 226, Rarity.COMMON, mage.cards.e.ElvishFury.class)); - cards.add(new SetCardInfo("Emerald Medallion", 283, Rarity.RARE, mage.cards.e.EmeraldMedallion.class)); - cards.add(new SetCardInfo("Emmessi Tome", 284, Rarity.RARE, mage.cards.e.EmmessiTome.class)); - cards.add(new SetCardInfo("Endless Scream", 132, Rarity.COMMON, mage.cards.e.EndlessScream.class)); - cards.add(new SetCardInfo("Energizer", 285, Rarity.RARE, mage.cards.e.Energizer.class)); - cards.add(new SetCardInfo("Enfeeblement", 133, Rarity.COMMON, mage.cards.e.Enfeeblement.class)); - cards.add(new SetCardInfo("Enraging Licid", 171, Rarity.UNCOMMON, mage.cards.e.EnragingLicid.class)); - cards.add(new SetCardInfo("Essence Bottle", 286, Rarity.UNCOMMON, mage.cards.e.EssenceBottle.class)); - cards.add(new SetCardInfo("Evincar's Justice", 134, Rarity.COMMON, mage.cards.e.EvincarsJustice.class)); - cards.add(new SetCardInfo("Excavator", 287, Rarity.UNCOMMON, mage.cards.e.Excavator.class)); - cards.add(new SetCardInfo("Extinction", 135, Rarity.RARE, mage.cards.e.Extinction.class)); - cards.add(new SetCardInfo("Fevered Convulsions", 136, Rarity.RARE, mage.cards.f.FeveredConvulsions.class)); - cards.add(new SetCardInfo("Field of Souls", 18, Rarity.RARE, mage.cards.f.FieldOfSouls.class)); - cards.add(new SetCardInfo("Fighting Drake", 63, Rarity.UNCOMMON, mage.cards.f.FightingDrake.class)); - cards.add(new SetCardInfo("Firefly", 172, Rarity.UNCOMMON, mage.cards.f.Firefly.class)); - cards.add(new SetCardInfo("Fireslinger", 173, Rarity.COMMON, mage.cards.f.Fireslinger.class)); - cards.add(new SetCardInfo("Flailing Drake", 227, Rarity.UNCOMMON, mage.cards.f.FlailingDrake.class)); - cards.add(new SetCardInfo("Flickering Ward", 19, Rarity.UNCOMMON, mage.cards.f.FlickeringWard.class)); - cards.add(new SetCardInfo("Flowstone Giant", 174, Rarity.COMMON, mage.cards.f.FlowstoneGiant.class)); - cards.add(new SetCardInfo("Flowstone Salamander", 175, Rarity.UNCOMMON, mage.cards.f.FlowstoneSalamander.class)); - cards.add(new SetCardInfo("Flowstone Sculpture", 288, Rarity.RARE, mage.cards.f.FlowstoneSculpture.class)); - cards.add(new SetCardInfo("Flowstone Wyvern", 176, Rarity.RARE, mage.cards.f.FlowstoneWyvern.class)); - cards.add(new SetCardInfo("Fool's Tome", 289, Rarity.RARE, mage.cards.f.FoolsTome.class)); - cards.add(new SetCardInfo("Forest", 347, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 348, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 349, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 350, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Frog Tongue", 228, Rarity.COMMON, mage.cards.f.FrogTongue.class)); - cards.add(new SetCardInfo("Fugitive Druid", 229, Rarity.RARE, mage.cards.f.FugitiveDruid.class)); - cards.add(new SetCardInfo("Furnace of Rath", 177, Rarity.RARE, mage.cards.f.FurnaceOfRath.class)); - cards.add(new SetCardInfo("Fylamarid", 64, Rarity.UNCOMMON, mage.cards.f.Fylamarid.class)); - cards.add(new SetCardInfo("Gallantry", 20, Rarity.UNCOMMON, mage.cards.g.Gallantry.class)); - cards.add(new SetCardInfo("Gaseous Form", 65, Rarity.COMMON, mage.cards.g.GaseousForm.class)); - cards.add(new SetCardInfo("Gerrard's Battle Cry", 21, Rarity.RARE, mage.cards.g.GerrardsBattleCry.class)); - cards.add(new SetCardInfo("Ghost Town", 318, Rarity.UNCOMMON, mage.cards.g.GhostTown.class)); - cards.add(new SetCardInfo("Giant Crab", 66, Rarity.COMMON, mage.cards.g.GiantCrab.class)); - cards.add(new SetCardInfo("Giant Strength", 178, Rarity.COMMON, mage.cards.g.GiantStrength.class)); - cards.add(new SetCardInfo("Goblin Bombardment", 179, Rarity.UNCOMMON, mage.cards.g.GoblinBombardment.class)); - cards.add(new SetCardInfo("Gravedigger", 137, Rarity.COMMON, mage.cards.g.Gravedigger.class)); - cards.add(new SetCardInfo("Grindstone", 290, Rarity.RARE, mage.cards.g.Grindstone.class)); - cards.add(new SetCardInfo("Hand to Hand", 180, Rarity.RARE, mage.cards.h.HandToHand.class)); - cards.add(new SetCardInfo("Hanna's Custody", 22, Rarity.RARE, mage.cards.h.HannasCustody.class)); - cards.add(new SetCardInfo("Harrow", 230, Rarity.UNCOMMON, mage.cards.h.Harrow.class)); - cards.add(new SetCardInfo("Havoc", 181, Rarity.UNCOMMON, mage.cards.h.Havoc.class)); - cards.add(new SetCardInfo("Heart Sliver", 182, Rarity.COMMON, mage.cards.h.HeartSliver.class)); - cards.add(new SetCardInfo("Heartwood Dryad", 231, Rarity.COMMON, mage.cards.h.HeartwoodDryad.class)); - cards.add(new SetCardInfo("Heartwood Giant", 232, Rarity.RARE, mage.cards.h.HeartwoodGiant.class)); - cards.add(new SetCardInfo("Heartwood Treefolk", 233, Rarity.UNCOMMON, mage.cards.h.HeartwoodTreefolk.class)); - cards.add(new SetCardInfo("Helm of Possession", 291, Rarity.RARE, mage.cards.h.HelmOfPossession.class)); - cards.add(new SetCardInfo("Hero's Resolve", 23, Rarity.COMMON, mage.cards.h.HerosResolve.class)); - cards.add(new SetCardInfo("Horned Sliver", 234, Rarity.UNCOMMON, mage.cards.h.HornedSliver.class)); - cards.add(new SetCardInfo("Horned Turtle", 67, Rarity.COMMON, mage.cards.h.HornedTurtle.class)); - cards.add(new SetCardInfo("Humility", 24, Rarity.RARE, mage.cards.h.Humility.class)); - cards.add(new SetCardInfo("Imps' Taunt", 138, Rarity.UNCOMMON, mage.cards.i.ImpsTaunt.class)); - cards.add(new SetCardInfo("Insight", 68, Rarity.UNCOMMON, mage.cards.i.Insight.class)); - cards.add(new SetCardInfo("Interdict", 69, Rarity.UNCOMMON, mage.cards.i.Interdict.class)); - cards.add(new SetCardInfo("Intuition", 70, Rarity.RARE, mage.cards.i.Intuition.class)); - cards.add(new SetCardInfo("Invulnerability", 25, Rarity.UNCOMMON, mage.cards.i.Invulnerability.class)); - cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 337, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 338, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Jackal Pup", 183, Rarity.UNCOMMON, mage.cards.j.JackalPup.class)); - cards.add(new SetCardInfo("Jet Medallion", 292, Rarity.RARE, mage.cards.j.JetMedallion.class)); - cards.add(new SetCardInfo("Jinxed Idol", 293, Rarity.RARE, mage.cards.j.JinxedIdol.class)); - cards.add(new SetCardInfo("Kezzerdrix", 139, Rarity.RARE, mage.cards.k.Kezzerdrix.class)); - cards.add(new SetCardInfo("Kindle", 184, Rarity.COMMON, mage.cards.k.Kindle.class)); - cards.add(new SetCardInfo("Knight of Dawn", 26, Rarity.UNCOMMON, mage.cards.k.KnightOfDawn.class)); - cards.add(new SetCardInfo("Knight of Dusk", 140, Rarity.UNCOMMON, mage.cards.k.KnightOfDusk.class)); - cards.add(new SetCardInfo("Krakilin", 235, Rarity.UNCOMMON, mage.cards.k.Krakilin.class)); - cards.add(new SetCardInfo("Leeching Licid", 141, Rarity.UNCOMMON, mage.cards.l.LeechingLicid.class)); - cards.add(new SetCardInfo("Legacy's Allure", 71, Rarity.UNCOMMON, mage.cards.l.LegacysAllure.class)); - cards.add(new SetCardInfo("Legerdemain", 72, Rarity.UNCOMMON, mage.cards.l.Legerdemain.class)); - cards.add(new SetCardInfo("Light of Day", 27, Rarity.UNCOMMON, mage.cards.l.LightOfDay.class)); - cards.add(new SetCardInfo("Lightning Blast", 185, Rarity.COMMON, mage.cards.l.LightningBlast.class)); - cards.add(new SetCardInfo("Lightning Elemental", 186, Rarity.COMMON, mage.cards.l.LightningElemental.class)); - cards.add(new SetCardInfo("Living Death", 142, Rarity.RARE, mage.cards.l.LivingDeath.class)); - cards.add(new SetCardInfo("Lobotomy", 267, Rarity.UNCOMMON, mage.cards.l.Lobotomy.class)); - cards.add(new SetCardInfo("Lotus Petal", 294, Rarity.COMMON, mage.cards.l.LotusPetal.class)); - cards.add(new SetCardInfo("Lowland Giant", 187, Rarity.COMMON, mage.cards.l.LowlandGiant.class)); - cards.add(new SetCardInfo("Maddening Imp", 143, Rarity.RARE, mage.cards.m.MaddeningImp.class)); - cards.add(new SetCardInfo("Magmasaur", 188, Rarity.RARE, mage.cards.m.Magmasaur.class)); - cards.add(new SetCardInfo("Mana Severance", 73, Rarity.RARE, mage.cards.m.ManaSeverance.class)); - cards.add(new SetCardInfo("Manakin", 296, Rarity.COMMON, mage.cards.m.Manakin.class)); - cards.add(new SetCardInfo("Manta Riders", 74, Rarity.COMMON, mage.cards.m.MantaRiders.class)); - cards.add(new SetCardInfo("Marble Titan", 28, Rarity.RARE, mage.cards.m.MarbleTitan.class)); - cards.add(new SetCardInfo("Marsh Lurker", 144, Rarity.COMMON, mage.cards.m.MarshLurker.class)); - cards.add(new SetCardInfo("Master Decoy", 29, Rarity.COMMON, mage.cards.m.MasterDecoy.class)); - cards.add(new SetCardInfo("Mawcor", 75, Rarity.RARE, mage.cards.m.Mawcor.class)); - cards.add(new SetCardInfo("Maze of Shadows", 319, Rarity.UNCOMMON, mage.cards.m.MazeOfShadows.class)); - cards.add(new SetCardInfo("Meditate", 76, Rarity.RARE, mage.cards.m.Meditate.class)); - cards.add(new SetCardInfo("Metallic Sliver", 297, Rarity.COMMON, mage.cards.m.MetallicSliver.class)); - cards.add(new SetCardInfo("Mindwhip Sliver", 145, Rarity.UNCOMMON, mage.cards.m.MindwhipSliver.class)); - cards.add(new SetCardInfo("Minion of the Wastes", 146, Rarity.RARE, mage.cards.m.MinionOfTheWastes.class)); - cards.add(new SetCardInfo("Mirri's Guile", 236, Rarity.RARE, mage.cards.m.MirrisGuile.class)); - cards.add(new SetCardInfo("Mnemonic Sliver", 77, Rarity.UNCOMMON, mage.cards.m.MnemonicSliver.class)); - cards.add(new SetCardInfo("Mogg Cannon", 298, Rarity.UNCOMMON, mage.cards.m.MoggCannon.class)); - cards.add(new SetCardInfo("Mogg Conscripts", 189, Rarity.COMMON, mage.cards.m.MoggConscripts.class)); - cards.add(new SetCardInfo("Mogg Fanatic", 190, Rarity.COMMON, mage.cards.m.MoggFanatic.class)); - cards.add(new SetCardInfo("Mogg Hollows", 320, Rarity.UNCOMMON, mage.cards.m.MoggHollows.class)); - cards.add(new SetCardInfo("Mogg Raider", 191, Rarity.COMMON, mage.cards.m.MoggRaider.class)); - cards.add(new SetCardInfo("Mogg Squad", 192, Rarity.UNCOMMON, mage.cards.m.MoggSquad.class)); - cards.add(new SetCardInfo("Mongrel Pack", 237, Rarity.RARE, mage.cards.m.MongrelPack.class)); - cards.add(new SetCardInfo("Mountain", 343, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 344, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 345, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 346, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mounted Archers", 30, Rarity.COMMON, mage.cards.m.MountedArchers.class)); - cards.add(new SetCardInfo("Muscle Sliver", 238, Rarity.COMMON, mage.cards.m.MuscleSliver.class)); - cards.add(new SetCardInfo("Natural Spring", 239, Rarity.COMMON, mage.cards.n.NaturalSpring.class)); - cards.add(new SetCardInfo("Nature's Revolt", 240, Rarity.RARE, mage.cards.n.NaturesRevolt.class)); - cards.add(new SetCardInfo("Needle Storm", 241, Rarity.UNCOMMON, mage.cards.n.NeedleStorm.class)); - cards.add(new SetCardInfo("Nurturing Licid", 242, Rarity.UNCOMMON, mage.cards.n.NurturingLicid.class)); - cards.add(new SetCardInfo("Opportunist", 194, Rarity.UNCOMMON, mage.cards.o.Opportunist.class)); - cards.add(new SetCardInfo("Oracle en-Vec", 31, Rarity.RARE, mage.cards.o.OracleEnVec.class)); - cards.add(new SetCardInfo("Orim's Prayer", 32, Rarity.UNCOMMON, mage.cards.o.OrimsPrayer.class)); - cards.add(new SetCardInfo("Orim, Samite Healer", 33, Rarity.RARE, mage.cards.o.OrimSamiteHealer.class)); - cards.add(new SetCardInfo("Overrun", 243, Rarity.UNCOMMON, mage.cards.o.Overrun.class)); - cards.add(new SetCardInfo("Pacifism", 34, Rarity.COMMON, mage.cards.p.Pacifism.class)); - cards.add(new SetCardInfo("Pallimud", 195, Rarity.RARE, mage.cards.p.Pallimud.class)); - cards.add(new SetCardInfo("Patchwork Gnomes", 299, Rarity.UNCOMMON, mage.cards.p.PatchworkGnomes.class)); - cards.add(new SetCardInfo("Pearl Medallion", 300, Rarity.RARE, mage.cards.p.PearlMedallion.class)); - cards.add(new SetCardInfo("Pegasus Refuge", 35, Rarity.RARE, mage.cards.p.PegasusRefuge.class)); - cards.add(new SetCardInfo("Perish", 147, Rarity.UNCOMMON, mage.cards.p.Perish.class)); - cards.add(new SetCardInfo("Phyrexian Grimoire", 301, Rarity.RARE, mage.cards.p.PhyrexianGrimoire.class)); - cards.add(new SetCardInfo("Phyrexian Hulk", 302, Rarity.UNCOMMON, mage.cards.p.PhyrexianHulk.class)); - cards.add(new SetCardInfo("Pincher Beetles", 244, Rarity.COMMON, mage.cards.p.PincherBeetles.class)); - cards.add(new SetCardInfo("Pine Barrens", 321, Rarity.RARE, mage.cards.p.PineBarrens.class)); - cards.add(new SetCardInfo("Pit Imp", 148, Rarity.COMMON, mage.cards.p.PitImp.class)); - cards.add(new SetCardInfo("Plains", 331, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 332, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 333, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 334, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Power Sink", 78, Rarity.COMMON, mage.cards.p.PowerSink.class)); - cards.add(new SetCardInfo("Precognition", 79, Rarity.RARE, mage.cards.p.Precognition.class)); - cards.add(new SetCardInfo("Propaganda", 80, Rarity.UNCOMMON, mage.cards.p.Propaganda.class)); - cards.add(new SetCardInfo("Puppet Strings", 304, Rarity.UNCOMMON, mage.cards.p.PuppetStrings.class)); - cards.add(new SetCardInfo("Quickening Licid", 36, Rarity.UNCOMMON, mage.cards.q.QuickeningLicid.class)); - cards.add(new SetCardInfo("Rain of Tears", 149, Rarity.UNCOMMON, mage.cards.r.RainOfTears.class)); - cards.add(new SetCardInfo("Rampant Growth", 245, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); - cards.add(new SetCardInfo("Ranger en-Vec", 268, Rarity.UNCOMMON, mage.cards.r.RangerEnVec.class)); - cards.add(new SetCardInfo("Rathi Dragon", 196, Rarity.RARE, mage.cards.r.RathiDragon.class)); - cards.add(new SetCardInfo("Rats of Rath", 150, Rarity.COMMON, mage.cards.r.RatsOfRath.class)); - cards.add(new SetCardInfo("Reality Anchor", 246, Rarity.COMMON, mage.cards.r.RealityAnchor.class)); - cards.add(new SetCardInfo("Reanimate", 151, Rarity.UNCOMMON, mage.cards.r.Reanimate.class)); - cards.add(new SetCardInfo("Reap", 247, Rarity.UNCOMMON, mage.cards.r.Reap.class)); - cards.add(new SetCardInfo("Reckless Spite", 152, Rarity.UNCOMMON, mage.cards.r.RecklessSpite.class)); - cards.add(new SetCardInfo("Recycle", 248, Rarity.RARE, mage.cards.r.Recycle.class)); - cards.add(new SetCardInfo("Reflecting Pool", 322, Rarity.RARE, mage.cards.r.ReflectingPool.class)); - cards.add(new SetCardInfo("Renegade Warlord", 197, Rarity.UNCOMMON, mage.cards.r.RenegadeWarlord.class)); - cards.add(new SetCardInfo("Repentance", 37, Rarity.UNCOMMON, mage.cards.r.Repentance.class)); - cards.add(new SetCardInfo("Respite", 249, Rarity.COMMON, mage.cards.r.Respite.class)); - cards.add(new SetCardInfo("Rolling Thunder", 198, Rarity.COMMON, mage.cards.r.RollingThunder.class)); - cards.add(new SetCardInfo("Root Maze", 250, Rarity.RARE, mage.cards.r.RootMaze.class)); - cards.add(new SetCardInfo("Rootbreaker Wurm", 251, Rarity.COMMON, mage.cards.r.RootbreakerWurm.class)); - cards.add(new SetCardInfo("Rootwalla", 252, Rarity.COMMON, mage.cards.r.Rootwalla.class)); - cards.add(new SetCardInfo("Rootwater Depths", 323, Rarity.UNCOMMON, mage.cards.r.RootwaterDepths.class)); - cards.add(new SetCardInfo("Rootwater Diver", 81, Rarity.UNCOMMON, mage.cards.r.RootwaterDiver.class)); - cards.add(new SetCardInfo("Rootwater Hunter", 82, Rarity.COMMON, mage.cards.r.RootwaterHunter.class)); - cards.add(new SetCardInfo("Rootwater Matriarch", 83, Rarity.RARE, mage.cards.r.RootwaterMatriarch.class)); - cards.add(new SetCardInfo("Rootwater Shaman", 84, Rarity.RARE, mage.cards.r.RootwaterShaman.class)); - cards.add(new SetCardInfo("Ruby Medallion", 305, Rarity.RARE, mage.cards.r.RubyMedallion.class)); - cards.add(new SetCardInfo("Sacred Guide", 38, Rarity.RARE, mage.cards.s.SacredGuide.class)); - cards.add(new SetCardInfo("Sadistic Glee", 153, Rarity.COMMON, mage.cards.s.SadisticGlee.class)); - cards.add(new SetCardInfo("Safeguard", 39, Rarity.RARE, mage.cards.s.Safeguard.class)); - cards.add(new SetCardInfo("Salt Flats", 324, Rarity.RARE, mage.cards.s.SaltFlats.class)); - cards.add(new SetCardInfo("Sandstone Warrior", 199, Rarity.COMMON, mage.cards.s.SandstoneWarrior.class)); - cards.add(new SetCardInfo("Sapphire Medallion", 306, Rarity.RARE, mage.cards.s.SapphireMedallion.class)); - cards.add(new SetCardInfo("Sarcomancy", 154, Rarity.RARE, mage.cards.s.Sarcomancy.class)); - cards.add(new SetCardInfo("Scabland", 325, Rarity.RARE, mage.cards.s.Scabland.class)); - cards.add(new SetCardInfo("Scalding Tongs", 307, Rarity.RARE, mage.cards.s.ScaldingTongs.class)); - cards.add(new SetCardInfo("Scorched Earth", 200, Rarity.RARE, mage.cards.s.ScorchedEarth.class)); - cards.add(new SetCardInfo("Scragnoth", 253, Rarity.UNCOMMON, mage.cards.s.Scragnoth.class)); - cards.add(new SetCardInfo("Screeching Harpy", 155, Rarity.UNCOMMON, mage.cards.s.ScreechingHarpy.class)); - cards.add(new SetCardInfo("Scroll Rack", 308, Rarity.RARE, mage.cards.s.ScrollRack.class)); - cards.add(new SetCardInfo("Sea Monster", 85, Rarity.COMMON, mage.cards.s.SeaMonster.class)); - cards.add(new SetCardInfo("Searing Touch", 201, Rarity.UNCOMMON, mage.cards.s.SearingTouch.class)); - cards.add(new SetCardInfo("Seeker of Skybreak", 254, Rarity.COMMON, mage.cards.s.SeekerOfSkybreak.class)); - cards.add(new SetCardInfo("Segmented Wurm", 269, Rarity.UNCOMMON, mage.cards.s.SegmentedWurm.class)); - cards.add(new SetCardInfo("Selenia, Dark Angel", 270, Rarity.RARE, mage.cards.s.SeleniaDarkAngel.class)); - cards.add(new SetCardInfo("Serene Offering", 40, Rarity.UNCOMMON, mage.cards.s.SereneOffering.class)); - cards.add(new SetCardInfo("Servant of Volrath", 156, Rarity.COMMON, mage.cards.s.ServantOfVolrath.class)); - cards.add(new SetCardInfo("Shadow Rift", 86, Rarity.COMMON, mage.cards.s.ShadowRift.class)); - cards.add(new SetCardInfo("Shadowstorm", 202, Rarity.UNCOMMON, mage.cards.s.Shadowstorm.class)); - cards.add(new SetCardInfo("Shatter", 203, Rarity.COMMON, mage.cards.s.Shatter.class)); - cards.add(new SetCardInfo("Shimmering Wings", 87, Rarity.COMMON, mage.cards.s.ShimmeringWings.class)); - cards.add(new SetCardInfo("Shocker", 204, Rarity.RARE, mage.cards.s.Shocker.class)); - cards.add(new SetCardInfo("Sky Spirit", 271, Rarity.UNCOMMON, mage.cards.s.SkySpirit.class)); - cards.add(new SetCardInfo("Skyshroud Condor", 88, Rarity.UNCOMMON, mage.cards.s.SkyshroudCondor.class)); - cards.add(new SetCardInfo("Skyshroud Elf", 255, Rarity.COMMON, mage.cards.s.SkyshroudElf.class)); - cards.add(new SetCardInfo("Skyshroud Forest", 326, Rarity.RARE, mage.cards.s.SkyshroudForest.class)); - cards.add(new SetCardInfo("Skyshroud Ranger", 256, Rarity.COMMON, mage.cards.s.SkyshroudRanger.class)); - cards.add(new SetCardInfo("Skyshroud Troll", 257, Rarity.COMMON, mage.cards.s.SkyshroudTroll.class)); - cards.add(new SetCardInfo("Skyshroud Vampire", 157, Rarity.UNCOMMON, mage.cards.s.SkyshroudVampire.class)); - cards.add(new SetCardInfo("Soltari Crusader", 41, Rarity.UNCOMMON, mage.cards.s.SoltariCrusader.class)); - cards.add(new SetCardInfo("Soltari Emissary", 42, Rarity.RARE, mage.cards.s.SoltariEmissary.class)); - cards.add(new SetCardInfo("Soltari Foot Soldier", 43, Rarity.COMMON, mage.cards.s.SoltariFootSoldier.class)); - cards.add(new SetCardInfo("Soltari Guerrillas", 272, Rarity.RARE, mage.cards.s.SoltariGuerrillas.class)); - cards.add(new SetCardInfo("Soltari Lancer", 44, Rarity.COMMON, mage.cards.s.SoltariLancer.class)); - cards.add(new SetCardInfo("Soltari Monk", 45, Rarity.UNCOMMON, mage.cards.s.SoltariMonk.class)); - cards.add(new SetCardInfo("Soltari Priest", 46, Rarity.UNCOMMON, mage.cards.s.SoltariPriest.class)); - cards.add(new SetCardInfo("Soltari Trooper", 47, Rarity.COMMON, mage.cards.s.SoltariTrooper.class)); - cards.add(new SetCardInfo("Souldrinker", 158, Rarity.UNCOMMON, mage.cards.s.Souldrinker.class)); - cards.add(new SetCardInfo("Spell Blast", 89, Rarity.COMMON, mage.cards.s.SpellBlast.class)); - cards.add(new SetCardInfo("Spike Drone", 258, Rarity.COMMON, mage.cards.s.SpikeDrone.class)); - cards.add(new SetCardInfo("Spinal Graft", 159, Rarity.COMMON, mage.cards.s.SpinalGraft.class)); - cards.add(new SetCardInfo("Spirit Mirror", 48, Rarity.RARE, mage.cards.s.SpiritMirror.class)); - cards.add(new SetCardInfo("Spontaneous Combustion", 273, Rarity.UNCOMMON, mage.cards.s.SpontaneousCombustion.class)); - cards.add(new SetCardInfo("Squee's Toy", 309, Rarity.COMMON, mage.cards.s.SqueesToy.class)); - cards.add(new SetCardInfo("Stalking Stones", 327, Rarity.UNCOMMON, mage.cards.s.StalkingStones.class)); - cards.add(new SetCardInfo("Starke of Rath", 205, Rarity.RARE, mage.cards.s.StarkeOfRath.class)); - cards.add(new SetCardInfo("Static Orb", 310, Rarity.RARE, mage.cards.s.StaticOrb.class)); - cards.add(new SetCardInfo("Staunch Defenders", 49, Rarity.UNCOMMON, mage.cards.s.StaunchDefenders.class)); - cards.add(new SetCardInfo("Steal Enchantment", 90, Rarity.UNCOMMON, mage.cards.s.StealEnchantment.class)); - cards.add(new SetCardInfo("Stinging Licid", 91, Rarity.UNCOMMON, mage.cards.s.StingingLicid.class)); - cards.add(new SetCardInfo("Stone Rain", 206, Rarity.COMMON, mage.cards.s.StoneRain.class)); - cards.add(new SetCardInfo("Storm Front", 259, Rarity.UNCOMMON, mage.cards.s.StormFront.class)); - cards.add(new SetCardInfo("Stun", 207, Rarity.COMMON, mage.cards.s.Stun.class)); - cards.add(new SetCardInfo("Sudden Impact", 208, Rarity.UNCOMMON, mage.cards.s.SuddenImpact.class)); - cards.add(new SetCardInfo("Swamp", 339, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 340, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 341, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 342, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Tahngarth's Rage", 209, Rarity.UNCOMMON, mage.cards.t.TahngarthsRage.class)); - cards.add(new SetCardInfo("Talon Sliver", 50, Rarity.COMMON, mage.cards.t.TalonSliver.class)); - cards.add(new SetCardInfo("Telethopter", 311, Rarity.UNCOMMON, mage.cards.t.Telethopter.class)); - cards.add(new SetCardInfo("Thalakos Dreamsower", 92, Rarity.UNCOMMON, mage.cards.t.ThalakosDreamsower.class)); - cards.add(new SetCardInfo("Thalakos Lowlands", 328, Rarity.UNCOMMON, mage.cards.t.ThalakosLowlands.class)); - cards.add(new SetCardInfo("Thalakos Mistfolk", 93, Rarity.COMMON, mage.cards.t.ThalakosMistfolk.class)); - cards.add(new SetCardInfo("Thalakos Seer", 94, Rarity.COMMON, mage.cards.t.ThalakosSeer.class)); - cards.add(new SetCardInfo("Thalakos Sentry", 95, Rarity.COMMON, mage.cards.t.ThalakosSentry.class)); - cards.add(new SetCardInfo("Thumbscrews", 312, Rarity.RARE, mage.cards.t.Thumbscrews.class)); - cards.add(new SetCardInfo("Time Ebb", 96, Rarity.COMMON, mage.cards.t.TimeEbb.class)); - cards.add(new SetCardInfo("Time Warp", 97, Rarity.RARE, mage.cards.t.TimeWarp.class)); - cards.add(new SetCardInfo("Tooth and Claw", 210, Rarity.RARE, mage.cards.t.ToothAndClaw.class)); - cards.add(new SetCardInfo("Torture Chamber", 313, Rarity.RARE, mage.cards.t.TortureChamber.class)); - cards.add(new SetCardInfo("Tradewind Rider", 98, Rarity.RARE, mage.cards.t.TradewindRider.class)); - cards.add(new SetCardInfo("Trained Armodon", 260, Rarity.COMMON, mage.cards.t.TrainedArmodon.class)); - cards.add(new SetCardInfo("Tranquility", 261, Rarity.COMMON, mage.cards.t.Tranquility.class)); - cards.add(new SetCardInfo("Trumpeting Armodon", 262, Rarity.UNCOMMON, mage.cards.t.TrumpetingArmodon.class)); - cards.add(new SetCardInfo("Twitch", 99, Rarity.COMMON, mage.cards.t.Twitch.class)); - cards.add(new SetCardInfo("Unstable Shapeshifter", 100, Rarity.RARE, mage.cards.u.UnstableShapeshifter.class)); - cards.add(new SetCardInfo("Vec Townships", 329, Rarity.UNCOMMON, mage.cards.v.VecTownships.class)); - cards.add(new SetCardInfo("Verdant Force", 263, Rarity.RARE, mage.cards.v.VerdantForce.class)); - cards.add(new SetCardInfo("Verdigris", 264, Rarity.UNCOMMON, mage.cards.v.Verdigris.class)); - cards.add(new SetCardInfo("Vhati il-Dal", 274, Rarity.RARE, mage.cards.v.VhatiIlDal.class)); - cards.add(new SetCardInfo("Volrath's Curse", 101, Rarity.COMMON, mage.cards.v.VolrathsCurse.class)); - cards.add(new SetCardInfo("Wall of Diffusion", 211, Rarity.COMMON, mage.cards.w.WallOfDiffusion.class)); - cards.add(new SetCardInfo("Warmth", 51, Rarity.UNCOMMON, mage.cards.w.Warmth.class)); - cards.add(new SetCardInfo("Wasteland", 330, Rarity.UNCOMMON, mage.cards.w.Wasteland.class)); - cards.add(new SetCardInfo("Watchdog", 314, Rarity.UNCOMMON, mage.cards.w.Watchdog.class)); - cards.add(new SetCardInfo("Whispers of the Muse", 103, Rarity.UNCOMMON, mage.cards.w.WhispersOfTheMuse.class)); - cards.add(new SetCardInfo("Wild Wurm", 212, Rarity.UNCOMMON, mage.cards.w.WildWurm.class)); - cards.add(new SetCardInfo("Wind Dancer", 104, Rarity.UNCOMMON, mage.cards.w.WindDancer.class)); - cards.add(new SetCardInfo("Wind Drake", 105, Rarity.COMMON, mage.cards.w.WindDrake.class)); - cards.add(new SetCardInfo("Winds of Rath", 52, Rarity.RARE, mage.cards.w.WindsOfRath.class)); - cards.add(new SetCardInfo("Winged Sliver", 106, Rarity.COMMON, mage.cards.w.WingedSliver.class)); - cards.add(new SetCardInfo("Winter's Grasp", 265, Rarity.UNCOMMON, mage.cards.w.WintersGrasp.class)); - cards.add(new SetCardInfo("Wood Sage", 275, Rarity.RARE, mage.cards.w.WoodSage.class)); - cards.add(new SetCardInfo("Worthy Cause", 53, Rarity.UNCOMMON, mage.cards.w.WorthyCause.class)); - } -} +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +public final class Tempest extends ExpansionSet { + + private static final Tempest instance = new Tempest(); + + public static Tempest getInstance() { + return instance; + } + + private Tempest() { + super("Tempest", "TMP", ExpansionSet.buildDate(1997, 10, 1), SetType.EXPANSION); + this.blockName = "Tempest"; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Abandon Hope", 107, Rarity.UNCOMMON, mage.cards.a.AbandonHope.class)); + cards.add(new SetCardInfo("Advance Scout", 1, Rarity.COMMON, mage.cards.a.AdvanceScout.class)); + cards.add(new SetCardInfo("Aftershock", 160, Rarity.COMMON, mage.cards.a.Aftershock.class)); + cards.add(new SetCardInfo("Altar of Dementia", 276, Rarity.RARE, mage.cards.a.AltarOfDementia.class)); + cards.add(new SetCardInfo("Aluren", 213, Rarity.RARE, mage.cards.a.Aluren.class)); + cards.add(new SetCardInfo("Ancient Runes", 161, Rarity.UNCOMMON, mage.cards.a.AncientRunes.class)); + cards.add(new SetCardInfo("Ancient Tomb", 315, Rarity.UNCOMMON, mage.cards.a.AncientTomb.class)); + cards.add(new SetCardInfo("Angelic Protector", 2, Rarity.UNCOMMON, mage.cards.a.AngelicProtector.class)); + cards.add(new SetCardInfo("Anoint", 3, Rarity.COMMON, mage.cards.a.Anoint.class)); + cards.add(new SetCardInfo("Apes of Rath", 214, Rarity.UNCOMMON, mage.cards.a.ApesOfRath.class)); + cards.add(new SetCardInfo("Apocalypse", 162, Rarity.RARE, mage.cards.a.Apocalypse.class)); + cards.add(new SetCardInfo("Armor Sliver", 4, Rarity.UNCOMMON, mage.cards.a.ArmorSliver.class)); + cards.add(new SetCardInfo("Armored Pegasus", 5, Rarity.COMMON, mage.cards.a.ArmoredPegasus.class)); + cards.add(new SetCardInfo("Auratog", 6, Rarity.RARE, mage.cards.a.Auratog.class)); + cards.add(new SetCardInfo("Avenging Angel", 7, Rarity.RARE, mage.cards.a.AvengingAngel.class)); + cards.add(new SetCardInfo("Barbed Sliver", 163, Rarity.UNCOMMON, mage.cards.b.BarbedSliver.class)); + cards.add(new SetCardInfo("Bayou Dragonfly", 215, Rarity.COMMON, mage.cards.b.BayouDragonfly.class)); + cards.add(new SetCardInfo("Bellowing Fiend", 108, Rarity.RARE, mage.cards.b.BellowingFiend.class)); + cards.add(new SetCardInfo("Benthic Behemoth", 54, Rarity.RARE, mage.cards.b.BenthicBehemoth.class)); + cards.add(new SetCardInfo("Blood Frenzy", 164, Rarity.COMMON, mage.cards.b.BloodFrenzy.class)); + cards.add(new SetCardInfo("Blood Pet", 109, Rarity.COMMON, mage.cards.b.BloodPet.class)); + cards.add(new SetCardInfo("Boil", 165, Rarity.UNCOMMON, mage.cards.b.Boil.class)); + cards.add(new SetCardInfo("Booby Trap", 277, Rarity.RARE, mage.cards.b.BoobyTrap.class)); + cards.add(new SetCardInfo("Bottle Gnomes", 278, Rarity.UNCOMMON, mage.cards.b.BottleGnomes.class)); + cards.add(new SetCardInfo("Bounty Hunter", 110, Rarity.RARE, mage.cards.b.BountyHunter.class)); + cards.add(new SetCardInfo("Broken Fall", 216, Rarity.COMMON, mage.cards.b.BrokenFall.class)); + cards.add(new SetCardInfo("Caldera Lake", 316, Rarity.RARE, mage.cards.c.CalderaLake.class)); + cards.add(new SetCardInfo("Canopy Spider", 217, Rarity.COMMON, mage.cards.c.CanopySpider.class)); + cards.add(new SetCardInfo("Canyon Drake", 166, Rarity.RARE, mage.cards.c.CanyonDrake.class)); + cards.add(new SetCardInfo("Canyon Wildcat", 167, Rarity.COMMON, mage.cards.c.CanyonWildcat.class)); + cards.add(new SetCardInfo("Capsize", 55, Rarity.COMMON, mage.cards.c.Capsize.class)); + cards.add(new SetCardInfo("Carrionette", 111, Rarity.RARE, mage.cards.c.Carrionette.class)); + cards.add(new SetCardInfo("Chaotic Goo", 168, Rarity.RARE, mage.cards.c.ChaoticGoo.class)); + cards.add(new SetCardInfo("Charging Rhino", 218, Rarity.UNCOMMON, mage.cards.c.ChargingRhino.class)); + cards.add(new SetCardInfo("Chill", 56, Rarity.UNCOMMON, mage.cards.c.Chill.class)); + cards.add(new SetCardInfo("Choke", 219, Rarity.UNCOMMON, mage.cards.c.Choke.class)); + cards.add(new SetCardInfo("Cinder Marsh", 317, Rarity.UNCOMMON, mage.cards.c.CinderMarsh.class)); + cards.add(new SetCardInfo("Circle of Protection: Black", 8, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlack.class)); + cards.add(new SetCardInfo("Circle of Protection: Blue", 9, Rarity.COMMON, mage.cards.c.CircleOfProtectionBlue.class)); + cards.add(new SetCardInfo("Circle of Protection: Green", 10, Rarity.COMMON, mage.cards.c.CircleOfProtectionGreen.class)); + cards.add(new SetCardInfo("Circle of Protection: Red", 11, Rarity.COMMON, mage.cards.c.CircleOfProtectionRed.class)); + cards.add(new SetCardInfo("Circle of Protection: Shadow", 12, Rarity.COMMON, mage.cards.c.CircleOfProtectionShadow.class)); + cards.add(new SetCardInfo("Circle of Protection: White", 13, Rarity.COMMON, mage.cards.c.CircleOfProtectionWhite.class)); + cards.add(new SetCardInfo("Clergy en-Vec", 14, Rarity.COMMON, mage.cards.c.ClergyEnVec.class)); + cards.add(new SetCardInfo("Clot Sliver", 112, Rarity.COMMON, mage.cards.c.ClotSliver.class)); + cards.add(new SetCardInfo("Cloudchaser Eagle", 15, Rarity.COMMON, mage.cards.c.CloudchaserEagle.class)); + cards.add(new SetCardInfo("Coercion", 113, Rarity.COMMON, mage.cards.c.Coercion.class)); + cards.add(new SetCardInfo("Coffin Queen", 114, Rarity.RARE, mage.cards.c.CoffinQueen.class)); + cards.add(new SetCardInfo("Coiled Tinviper", 279, Rarity.COMMON, mage.cards.c.CoiledTinviper.class)); + cards.add(new SetCardInfo("Cold Storage", 280, Rarity.RARE, mage.cards.c.ColdStorage.class)); + cards.add(new SetCardInfo("Commander Greven il-Vec", 115, Rarity.RARE, mage.cards.c.CommanderGrevenIlVec.class)); + cards.add(new SetCardInfo("Corpse Dance", 116, Rarity.RARE, mage.cards.c.CorpseDance.class)); + cards.add(new SetCardInfo("Counterspell", 57, Rarity.COMMON, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Crazed Armodon", 220, Rarity.RARE, mage.cards.c.CrazedArmodon.class)); + cards.add(new SetCardInfo("Crown of Flames", 169, Rarity.COMMON, mage.cards.c.CrownOfFlames.class)); + cards.add(new SetCardInfo("Cursed Scroll", 281, Rarity.RARE, mage.cards.c.CursedScroll.class)); + cards.add(new SetCardInfo("Dark Banishing", 117, Rarity.COMMON, mage.cards.d.DarkBanishing.class)); + cards.add(new SetCardInfo("Dark Ritual", 118, Rarity.COMMON, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Darkling Stalker", 119, Rarity.COMMON, mage.cards.d.DarklingStalker.class)); + cards.add(new SetCardInfo("Dauthi Embrace", 120, Rarity.UNCOMMON, mage.cards.d.DauthiEmbrace.class)); + cards.add(new SetCardInfo("Dauthi Ghoul", 121, Rarity.UNCOMMON, mage.cards.d.DauthiGhoul.class)); + cards.add(new SetCardInfo("Dauthi Horror", 122, Rarity.COMMON, mage.cards.d.DauthiHorror.class)); + cards.add(new SetCardInfo("Dauthi Marauder", 123, Rarity.COMMON, mage.cards.d.DauthiMarauder.class)); + cards.add(new SetCardInfo("Dauthi Mercenary", 124, Rarity.UNCOMMON, mage.cards.d.DauthiMercenary.class)); + cards.add(new SetCardInfo("Dauthi Mindripper", 125, Rarity.UNCOMMON, mage.cards.d.DauthiMindripper.class)); + cards.add(new SetCardInfo("Dauthi Slayer", 126, Rarity.COMMON, mage.cards.d.DauthiSlayer.class)); + cards.add(new SetCardInfo("Deadshot", 170, Rarity.RARE, mage.cards.d.Deadshot.class)); + cards.add(new SetCardInfo("Death Pits of Rath", 127, Rarity.RARE, mage.cards.d.DeathPitsOfRath.class)); + cards.add(new SetCardInfo("Diabolic Edict", 128, Rarity.COMMON, mage.cards.d.DiabolicEdict.class)); + cards.add(new SetCardInfo("Dirtcowl Wurm", 221, Rarity.RARE, mage.cards.d.DirtcowlWurm.class)); + cards.add(new SetCardInfo("Disenchant", 16, Rarity.COMMON, mage.cards.d.Disenchant.class)); + cards.add(new SetCardInfo("Dismiss", 58, Rarity.UNCOMMON, mage.cards.d.Dismiss.class)); + cards.add(new SetCardInfo("Disturbed Burial", 129, Rarity.COMMON, mage.cards.d.DisturbedBurial.class)); + cards.add(new SetCardInfo("Dracoplasm", 266, Rarity.RARE, mage.cards.d.Dracoplasm.class)); + cards.add(new SetCardInfo("Dread of Night", 130, Rarity.UNCOMMON, mage.cards.d.DreadOfNight.class)); + cards.add(new SetCardInfo("Dream Cache", 59, Rarity.COMMON, mage.cards.d.DreamCache.class)); + cards.add(new SetCardInfo("Dregs of Sorrow", 131, Rarity.RARE, mage.cards.d.DregsOfSorrow.class)); + cards.add(new SetCardInfo("Duplicity", 60, Rarity.RARE, mage.cards.d.Duplicity.class)); + cards.add(new SetCardInfo("Earthcraft", 222, Rarity.RARE, mage.cards.e.Earthcraft.class)); + cards.add(new SetCardInfo("Echo Chamber", 282, Rarity.RARE, mage.cards.e.EchoChamber.class)); + cards.add(new SetCardInfo("Eladamri's Vineyard", 223, Rarity.RARE, mage.cards.e.EladamrisVineyard.class)); + cards.add(new SetCardInfo("Eladamri, Lord of Leaves", 224, Rarity.RARE, mage.cards.e.EladamriLordOfLeaves.class)); + cards.add(new SetCardInfo("Elite Javelineer", 17, Rarity.COMMON, mage.cards.e.EliteJavelineer.class)); + cards.add(new SetCardInfo("Elven Warhounds", 225, Rarity.RARE, mage.cards.e.ElvenWarhounds.class)); + cards.add(new SetCardInfo("Elvish Fury", 226, Rarity.COMMON, mage.cards.e.ElvishFury.class)); + cards.add(new SetCardInfo("Emerald Medallion", 283, Rarity.RARE, mage.cards.e.EmeraldMedallion.class)); + cards.add(new SetCardInfo("Emmessi Tome", 284, Rarity.RARE, mage.cards.e.EmmessiTome.class)); + cards.add(new SetCardInfo("Endless Scream", 132, Rarity.COMMON, mage.cards.e.EndlessScream.class)); + cards.add(new SetCardInfo("Energizer", 285, Rarity.RARE, mage.cards.e.Energizer.class)); + cards.add(new SetCardInfo("Enfeeblement", 133, Rarity.COMMON, mage.cards.e.Enfeeblement.class)); + cards.add(new SetCardInfo("Enraging Licid", 171, Rarity.UNCOMMON, mage.cards.e.EnragingLicid.class)); + cards.add(new SetCardInfo("Essence Bottle", 286, Rarity.UNCOMMON, mage.cards.e.EssenceBottle.class)); + cards.add(new SetCardInfo("Evincar's Justice", 134, Rarity.COMMON, mage.cards.e.EvincarsJustice.class)); + cards.add(new SetCardInfo("Excavator", 287, Rarity.UNCOMMON, mage.cards.e.Excavator.class)); + cards.add(new SetCardInfo("Extinction", 135, Rarity.RARE, mage.cards.e.Extinction.class)); + cards.add(new SetCardInfo("Fevered Convulsions", 136, Rarity.RARE, mage.cards.f.FeveredConvulsions.class)); + cards.add(new SetCardInfo("Field of Souls", 18, Rarity.RARE, mage.cards.f.FieldOfSouls.class)); + cards.add(new SetCardInfo("Fighting Drake", 63, Rarity.UNCOMMON, mage.cards.f.FightingDrake.class)); + cards.add(new SetCardInfo("Firefly", 172, Rarity.UNCOMMON, mage.cards.f.Firefly.class)); + cards.add(new SetCardInfo("Fireslinger", 173, Rarity.COMMON, mage.cards.f.Fireslinger.class)); + cards.add(new SetCardInfo("Flailing Drake", 227, Rarity.UNCOMMON, mage.cards.f.FlailingDrake.class)); + cards.add(new SetCardInfo("Flickering Ward", 19, Rarity.UNCOMMON, mage.cards.f.FlickeringWard.class)); + cards.add(new SetCardInfo("Flowstone Giant", 174, Rarity.COMMON, mage.cards.f.FlowstoneGiant.class)); + cards.add(new SetCardInfo("Flowstone Salamander", 175, Rarity.UNCOMMON, mage.cards.f.FlowstoneSalamander.class)); + cards.add(new SetCardInfo("Flowstone Sculpture", 288, Rarity.RARE, mage.cards.f.FlowstoneSculpture.class)); + cards.add(new SetCardInfo("Flowstone Wyvern", 176, Rarity.RARE, mage.cards.f.FlowstoneWyvern.class)); + cards.add(new SetCardInfo("Fool's Tome", 289, Rarity.RARE, mage.cards.f.FoolsTome.class)); + cards.add(new SetCardInfo("Forest", 347, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 348, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 349, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 350, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Frog Tongue", 228, Rarity.COMMON, mage.cards.f.FrogTongue.class)); + cards.add(new SetCardInfo("Fugitive Druid", 229, Rarity.RARE, mage.cards.f.FugitiveDruid.class)); + cards.add(new SetCardInfo("Furnace of Rath", 177, Rarity.RARE, mage.cards.f.FurnaceOfRath.class)); + cards.add(new SetCardInfo("Fylamarid", 64, Rarity.UNCOMMON, mage.cards.f.Fylamarid.class)); + cards.add(new SetCardInfo("Gallantry", 20, Rarity.UNCOMMON, mage.cards.g.Gallantry.class)); + cards.add(new SetCardInfo("Gaseous Form", 65, Rarity.COMMON, mage.cards.g.GaseousForm.class)); + cards.add(new SetCardInfo("Gerrard's Battle Cry", 21, Rarity.RARE, mage.cards.g.GerrardsBattleCry.class)); + cards.add(new SetCardInfo("Ghost Town", 318, Rarity.UNCOMMON, mage.cards.g.GhostTown.class)); + cards.add(new SetCardInfo("Giant Crab", 66, Rarity.COMMON, mage.cards.g.GiantCrab.class)); + cards.add(new SetCardInfo("Giant Strength", 178, Rarity.COMMON, mage.cards.g.GiantStrength.class)); + cards.add(new SetCardInfo("Goblin Bombardment", 179, Rarity.UNCOMMON, mage.cards.g.GoblinBombardment.class)); + cards.add(new SetCardInfo("Gravedigger", 137, Rarity.COMMON, mage.cards.g.Gravedigger.class)); + cards.add(new SetCardInfo("Grindstone", 290, Rarity.RARE, mage.cards.g.Grindstone.class)); + cards.add(new SetCardInfo("Hand to Hand", 180, Rarity.RARE, mage.cards.h.HandToHand.class)); + cards.add(new SetCardInfo("Hanna's Custody", 22, Rarity.RARE, mage.cards.h.HannasCustody.class)); + cards.add(new SetCardInfo("Harrow", 230, Rarity.UNCOMMON, mage.cards.h.Harrow.class)); + cards.add(new SetCardInfo("Havoc", 181, Rarity.UNCOMMON, mage.cards.h.Havoc.class)); + cards.add(new SetCardInfo("Heart Sliver", 182, Rarity.COMMON, mage.cards.h.HeartSliver.class)); + cards.add(new SetCardInfo("Heartwood Dryad", 231, Rarity.COMMON, mage.cards.h.HeartwoodDryad.class)); + cards.add(new SetCardInfo("Heartwood Giant", 232, Rarity.RARE, mage.cards.h.HeartwoodGiant.class)); + cards.add(new SetCardInfo("Heartwood Treefolk", 233, Rarity.UNCOMMON, mage.cards.h.HeartwoodTreefolk.class)); + cards.add(new SetCardInfo("Helm of Possession", 291, Rarity.RARE, mage.cards.h.HelmOfPossession.class)); + cards.add(new SetCardInfo("Hero's Resolve", 23, Rarity.COMMON, mage.cards.h.HerosResolve.class)); + cards.add(new SetCardInfo("Horned Sliver", 234, Rarity.UNCOMMON, mage.cards.h.HornedSliver.class)); + cards.add(new SetCardInfo("Horned Turtle", 67, Rarity.COMMON, mage.cards.h.HornedTurtle.class)); + cards.add(new SetCardInfo("Humility", 24, Rarity.RARE, mage.cards.h.Humility.class)); + cards.add(new SetCardInfo("Imps' Taunt", 138, Rarity.UNCOMMON, mage.cards.i.ImpsTaunt.class)); + cards.add(new SetCardInfo("Insight", 68, Rarity.UNCOMMON, mage.cards.i.Insight.class)); + cards.add(new SetCardInfo("Interdict", 69, Rarity.UNCOMMON, mage.cards.i.Interdict.class)); + cards.add(new SetCardInfo("Intuition", 70, Rarity.RARE, mage.cards.i.Intuition.class)); + cards.add(new SetCardInfo("Invulnerability", 25, Rarity.UNCOMMON, mage.cards.i.Invulnerability.class)); + cards.add(new SetCardInfo("Island", 335, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 336, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 337, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 338, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jackal Pup", 183, Rarity.UNCOMMON, mage.cards.j.JackalPup.class)); + cards.add(new SetCardInfo("Jet Medallion", 292, Rarity.RARE, mage.cards.j.JetMedallion.class)); + cards.add(new SetCardInfo("Jinxed Idol", 293, Rarity.RARE, mage.cards.j.JinxedIdol.class)); + cards.add(new SetCardInfo("Kezzerdrix", 139, Rarity.RARE, mage.cards.k.Kezzerdrix.class)); + cards.add(new SetCardInfo("Kindle", 184, Rarity.COMMON, mage.cards.k.Kindle.class)); + cards.add(new SetCardInfo("Knight of Dawn", 26, Rarity.UNCOMMON, mage.cards.k.KnightOfDawn.class)); + cards.add(new SetCardInfo("Knight of Dusk", 140, Rarity.UNCOMMON, mage.cards.k.KnightOfDusk.class)); + cards.add(new SetCardInfo("Krakilin", 235, Rarity.UNCOMMON, mage.cards.k.Krakilin.class)); + cards.add(new SetCardInfo("Leeching Licid", 141, Rarity.UNCOMMON, mage.cards.l.LeechingLicid.class)); + cards.add(new SetCardInfo("Legacy's Allure", 71, Rarity.UNCOMMON, mage.cards.l.LegacysAllure.class)); + cards.add(new SetCardInfo("Legerdemain", 72, Rarity.UNCOMMON, mage.cards.l.Legerdemain.class)); + cards.add(new SetCardInfo("Light of Day", 27, Rarity.UNCOMMON, mage.cards.l.LightOfDay.class)); + cards.add(new SetCardInfo("Lightning Blast", 185, Rarity.COMMON, mage.cards.l.LightningBlast.class)); + cards.add(new SetCardInfo("Lightning Elemental", 186, Rarity.COMMON, mage.cards.l.LightningElemental.class)); + cards.add(new SetCardInfo("Living Death", 142, Rarity.RARE, mage.cards.l.LivingDeath.class)); + cards.add(new SetCardInfo("Lobotomy", 267, Rarity.UNCOMMON, mage.cards.l.Lobotomy.class)); + cards.add(new SetCardInfo("Lotus Petal", 294, Rarity.COMMON, mage.cards.l.LotusPetal.class)); + cards.add(new SetCardInfo("Lowland Giant", 187, Rarity.COMMON, mage.cards.l.LowlandGiant.class)); + cards.add(new SetCardInfo("Maddening Imp", 143, Rarity.RARE, mage.cards.m.MaddeningImp.class)); + cards.add(new SetCardInfo("Magmasaur", 188, Rarity.RARE, mage.cards.m.Magmasaur.class)); + cards.add(new SetCardInfo("Mana Severance", 73, Rarity.RARE, mage.cards.m.ManaSeverance.class)); + cards.add(new SetCardInfo("Manakin", 296, Rarity.COMMON, mage.cards.m.Manakin.class)); + cards.add(new SetCardInfo("Manta Riders", 74, Rarity.COMMON, mage.cards.m.MantaRiders.class)); + cards.add(new SetCardInfo("Marble Titan", 28, Rarity.RARE, mage.cards.m.MarbleTitan.class)); + cards.add(new SetCardInfo("Marsh Lurker", 144, Rarity.COMMON, mage.cards.m.MarshLurker.class)); + cards.add(new SetCardInfo("Master Decoy", 29, Rarity.COMMON, mage.cards.m.MasterDecoy.class)); + cards.add(new SetCardInfo("Mawcor", 75, Rarity.RARE, mage.cards.m.Mawcor.class)); + cards.add(new SetCardInfo("Maze of Shadows", 319, Rarity.UNCOMMON, mage.cards.m.MazeOfShadows.class)); + cards.add(new SetCardInfo("Meditate", 76, Rarity.RARE, mage.cards.m.Meditate.class)); + cards.add(new SetCardInfo("Metallic Sliver", 297, Rarity.COMMON, mage.cards.m.MetallicSliver.class)); + cards.add(new SetCardInfo("Mindwhip Sliver", 145, Rarity.UNCOMMON, mage.cards.m.MindwhipSliver.class)); + cards.add(new SetCardInfo("Minion of the Wastes", 146, Rarity.RARE, mage.cards.m.MinionOfTheWastes.class)); + cards.add(new SetCardInfo("Mirri's Guile", 236, Rarity.RARE, mage.cards.m.MirrisGuile.class)); + cards.add(new SetCardInfo("Mnemonic Sliver", 77, Rarity.UNCOMMON, mage.cards.m.MnemonicSliver.class)); + cards.add(new SetCardInfo("Mogg Cannon", 298, Rarity.UNCOMMON, mage.cards.m.MoggCannon.class)); + cards.add(new SetCardInfo("Mogg Conscripts", 189, Rarity.COMMON, mage.cards.m.MoggConscripts.class)); + cards.add(new SetCardInfo("Mogg Fanatic", 190, Rarity.COMMON, mage.cards.m.MoggFanatic.class)); + cards.add(new SetCardInfo("Mogg Hollows", 320, Rarity.UNCOMMON, mage.cards.m.MoggHollows.class)); + cards.add(new SetCardInfo("Mogg Raider", 191, Rarity.COMMON, mage.cards.m.MoggRaider.class)); + cards.add(new SetCardInfo("Mogg Squad", 192, Rarity.UNCOMMON, mage.cards.m.MoggSquad.class)); + cards.add(new SetCardInfo("Mongrel Pack", 237, Rarity.RARE, mage.cards.m.MongrelPack.class)); + cards.add(new SetCardInfo("Mountain", 343, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 344, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 345, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 346, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mounted Archers", 30, Rarity.COMMON, mage.cards.m.MountedArchers.class)); + cards.add(new SetCardInfo("Muscle Sliver", 238, Rarity.COMMON, mage.cards.m.MuscleSliver.class)); + cards.add(new SetCardInfo("Natural Spring", 239, Rarity.COMMON, mage.cards.n.NaturalSpring.class)); + cards.add(new SetCardInfo("Nature's Revolt", 240, Rarity.RARE, mage.cards.n.NaturesRevolt.class)); + cards.add(new SetCardInfo("Needle Storm", 241, Rarity.UNCOMMON, mage.cards.n.NeedleStorm.class)); + cards.add(new SetCardInfo("Nurturing Licid", 242, Rarity.UNCOMMON, mage.cards.n.NurturingLicid.class)); + cards.add(new SetCardInfo("Opportunist", 194, Rarity.UNCOMMON, mage.cards.o.Opportunist.class)); + cards.add(new SetCardInfo("Oracle en-Vec", 31, Rarity.RARE, mage.cards.o.OracleEnVec.class)); + cards.add(new SetCardInfo("Orim's Prayer", 32, Rarity.UNCOMMON, mage.cards.o.OrimsPrayer.class)); + cards.add(new SetCardInfo("Orim, Samite Healer", 33, Rarity.RARE, mage.cards.o.OrimSamiteHealer.class)); + cards.add(new SetCardInfo("Overrun", 243, Rarity.UNCOMMON, mage.cards.o.Overrun.class)); + cards.add(new SetCardInfo("Pacifism", 34, Rarity.COMMON, mage.cards.p.Pacifism.class)); + cards.add(new SetCardInfo("Pallimud", 195, Rarity.RARE, mage.cards.p.Pallimud.class)); + cards.add(new SetCardInfo("Patchwork Gnomes", 299, Rarity.UNCOMMON, mage.cards.p.PatchworkGnomes.class)); + cards.add(new SetCardInfo("Pearl Medallion", 300, Rarity.RARE, mage.cards.p.PearlMedallion.class)); + cards.add(new SetCardInfo("Pegasus Refuge", 35, Rarity.RARE, mage.cards.p.PegasusRefuge.class)); + cards.add(new SetCardInfo("Perish", 147, Rarity.UNCOMMON, mage.cards.p.Perish.class)); + cards.add(new SetCardInfo("Phyrexian Grimoire", 301, Rarity.RARE, mage.cards.p.PhyrexianGrimoire.class)); + cards.add(new SetCardInfo("Phyrexian Hulk", 302, Rarity.UNCOMMON, mage.cards.p.PhyrexianHulk.class)); + cards.add(new SetCardInfo("Pincher Beetles", 244, Rarity.COMMON, mage.cards.p.PincherBeetles.class)); + cards.add(new SetCardInfo("Pine Barrens", 321, Rarity.RARE, mage.cards.p.PineBarrens.class)); + cards.add(new SetCardInfo("Pit Imp", 148, Rarity.COMMON, mage.cards.p.PitImp.class)); + cards.add(new SetCardInfo("Plains", 331, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 332, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 333, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 334, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Power Sink", 78, Rarity.COMMON, mage.cards.p.PowerSink.class)); + cards.add(new SetCardInfo("Precognition", 79, Rarity.RARE, mage.cards.p.Precognition.class)); + cards.add(new SetCardInfo("Propaganda", 80, Rarity.UNCOMMON, mage.cards.p.Propaganda.class)); + cards.add(new SetCardInfo("Puppet Strings", 304, Rarity.UNCOMMON, mage.cards.p.PuppetStrings.class)); + cards.add(new SetCardInfo("Quickening Licid", 36, Rarity.UNCOMMON, mage.cards.q.QuickeningLicid.class)); + cards.add(new SetCardInfo("Rain of Tears", 149, Rarity.UNCOMMON, mage.cards.r.RainOfTears.class)); + cards.add(new SetCardInfo("Rampant Growth", 245, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); + cards.add(new SetCardInfo("Ranger en-Vec", 268, Rarity.UNCOMMON, mage.cards.r.RangerEnVec.class)); + cards.add(new SetCardInfo("Rathi Dragon", 196, Rarity.RARE, mage.cards.r.RathiDragon.class)); + cards.add(new SetCardInfo("Rats of Rath", 150, Rarity.COMMON, mage.cards.r.RatsOfRath.class)); + cards.add(new SetCardInfo("Reality Anchor", 246, Rarity.COMMON, mage.cards.r.RealityAnchor.class)); + cards.add(new SetCardInfo("Reanimate", 151, Rarity.UNCOMMON, mage.cards.r.Reanimate.class)); + cards.add(new SetCardInfo("Reap", 247, Rarity.UNCOMMON, mage.cards.r.Reap.class)); + cards.add(new SetCardInfo("Reckless Spite", 152, Rarity.UNCOMMON, mage.cards.r.RecklessSpite.class)); + cards.add(new SetCardInfo("Recycle", 248, Rarity.RARE, mage.cards.r.Recycle.class)); + cards.add(new SetCardInfo("Reflecting Pool", 322, Rarity.RARE, mage.cards.r.ReflectingPool.class)); + cards.add(new SetCardInfo("Renegade Warlord", 197, Rarity.UNCOMMON, mage.cards.r.RenegadeWarlord.class)); + cards.add(new SetCardInfo("Repentance", 37, Rarity.UNCOMMON, mage.cards.r.Repentance.class)); + cards.add(new SetCardInfo("Respite", 249, Rarity.COMMON, mage.cards.r.Respite.class)); + cards.add(new SetCardInfo("Rolling Thunder", 198, Rarity.COMMON, mage.cards.r.RollingThunder.class)); + cards.add(new SetCardInfo("Root Maze", 250, Rarity.RARE, mage.cards.r.RootMaze.class)); + cards.add(new SetCardInfo("Rootbreaker Wurm", 251, Rarity.COMMON, mage.cards.r.RootbreakerWurm.class)); + cards.add(new SetCardInfo("Rootwalla", 252, Rarity.COMMON, mage.cards.r.Rootwalla.class)); + cards.add(new SetCardInfo("Rootwater Depths", 323, Rarity.UNCOMMON, mage.cards.r.RootwaterDepths.class)); + cards.add(new SetCardInfo("Rootwater Diver", 81, Rarity.UNCOMMON, mage.cards.r.RootwaterDiver.class)); + cards.add(new SetCardInfo("Rootwater Hunter", 82, Rarity.COMMON, mage.cards.r.RootwaterHunter.class)); + cards.add(new SetCardInfo("Rootwater Matriarch", 83, Rarity.RARE, mage.cards.r.RootwaterMatriarch.class)); + cards.add(new SetCardInfo("Rootwater Shaman", 84, Rarity.RARE, mage.cards.r.RootwaterShaman.class)); + cards.add(new SetCardInfo("Ruby Medallion", 305, Rarity.RARE, mage.cards.r.RubyMedallion.class)); + cards.add(new SetCardInfo("Sacred Guide", 38, Rarity.RARE, mage.cards.s.SacredGuide.class)); + cards.add(new SetCardInfo("Sadistic Glee", 153, Rarity.COMMON, mage.cards.s.SadisticGlee.class)); + cards.add(new SetCardInfo("Safeguard", 39, Rarity.RARE, mage.cards.s.Safeguard.class)); + cards.add(new SetCardInfo("Salt Flats", 324, Rarity.RARE, mage.cards.s.SaltFlats.class)); + cards.add(new SetCardInfo("Sandstone Warrior", 199, Rarity.COMMON, mage.cards.s.SandstoneWarrior.class)); + cards.add(new SetCardInfo("Sapphire Medallion", 306, Rarity.RARE, mage.cards.s.SapphireMedallion.class)); + cards.add(new SetCardInfo("Sarcomancy", 154, Rarity.RARE, mage.cards.s.Sarcomancy.class)); + cards.add(new SetCardInfo("Scabland", 325, Rarity.RARE, mage.cards.s.Scabland.class)); + cards.add(new SetCardInfo("Scalding Tongs", 307, Rarity.RARE, mage.cards.s.ScaldingTongs.class)); + cards.add(new SetCardInfo("Scorched Earth", 200, Rarity.RARE, mage.cards.s.ScorchedEarth.class)); + cards.add(new SetCardInfo("Scragnoth", 253, Rarity.UNCOMMON, mage.cards.s.Scragnoth.class)); + cards.add(new SetCardInfo("Screeching Harpy", 155, Rarity.UNCOMMON, mage.cards.s.ScreechingHarpy.class)); + cards.add(new SetCardInfo("Scroll Rack", 308, Rarity.RARE, mage.cards.s.ScrollRack.class)); + cards.add(new SetCardInfo("Sea Monster", 85, Rarity.COMMON, mage.cards.s.SeaMonster.class)); + cards.add(new SetCardInfo("Searing Touch", 201, Rarity.UNCOMMON, mage.cards.s.SearingTouch.class)); + cards.add(new SetCardInfo("Seeker of Skybreak", 254, Rarity.COMMON, mage.cards.s.SeekerOfSkybreak.class)); + cards.add(new SetCardInfo("Segmented Wurm", 269, Rarity.UNCOMMON, mage.cards.s.SegmentedWurm.class)); + cards.add(new SetCardInfo("Selenia, Dark Angel", 270, Rarity.RARE, mage.cards.s.SeleniaDarkAngel.class)); + cards.add(new SetCardInfo("Serene Offering", 40, Rarity.UNCOMMON, mage.cards.s.SereneOffering.class)); + cards.add(new SetCardInfo("Servant of Volrath", 156, Rarity.COMMON, mage.cards.s.ServantOfVolrath.class)); + cards.add(new SetCardInfo("Shadow Rift", 86, Rarity.COMMON, mage.cards.s.ShadowRift.class)); + cards.add(new SetCardInfo("Shadowstorm", 202, Rarity.UNCOMMON, mage.cards.s.Shadowstorm.class)); + cards.add(new SetCardInfo("Shatter", 203, Rarity.COMMON, mage.cards.s.Shatter.class)); + cards.add(new SetCardInfo("Shimmering Wings", 87, Rarity.COMMON, mage.cards.s.ShimmeringWings.class)); + cards.add(new SetCardInfo("Shocker", 204, Rarity.RARE, mage.cards.s.Shocker.class)); + cards.add(new SetCardInfo("Sky Spirit", 271, Rarity.UNCOMMON, mage.cards.s.SkySpirit.class)); + cards.add(new SetCardInfo("Skyshroud Condor", 88, Rarity.UNCOMMON, mage.cards.s.SkyshroudCondor.class)); + cards.add(new SetCardInfo("Skyshroud Elf", 255, Rarity.COMMON, mage.cards.s.SkyshroudElf.class)); + cards.add(new SetCardInfo("Skyshroud Forest", 326, Rarity.RARE, mage.cards.s.SkyshroudForest.class)); + cards.add(new SetCardInfo("Skyshroud Ranger", 256, Rarity.COMMON, mage.cards.s.SkyshroudRanger.class)); + cards.add(new SetCardInfo("Skyshroud Troll", 257, Rarity.COMMON, mage.cards.s.SkyshroudTroll.class)); + cards.add(new SetCardInfo("Skyshroud Vampire", 157, Rarity.UNCOMMON, mage.cards.s.SkyshroudVampire.class)); + cards.add(new SetCardInfo("Soltari Crusader", 41, Rarity.UNCOMMON, mage.cards.s.SoltariCrusader.class)); + cards.add(new SetCardInfo("Soltari Emissary", 42, Rarity.RARE, mage.cards.s.SoltariEmissary.class)); + cards.add(new SetCardInfo("Soltari Foot Soldier", 43, Rarity.COMMON, mage.cards.s.SoltariFootSoldier.class)); + cards.add(new SetCardInfo("Soltari Guerrillas", 272, Rarity.RARE, mage.cards.s.SoltariGuerrillas.class)); + cards.add(new SetCardInfo("Soltari Lancer", 44, Rarity.COMMON, mage.cards.s.SoltariLancer.class)); + cards.add(new SetCardInfo("Soltari Monk", 45, Rarity.UNCOMMON, mage.cards.s.SoltariMonk.class)); + cards.add(new SetCardInfo("Soltari Priest", 46, Rarity.UNCOMMON, mage.cards.s.SoltariPriest.class)); + cards.add(new SetCardInfo("Soltari Trooper", 47, Rarity.COMMON, mage.cards.s.SoltariTrooper.class)); + cards.add(new SetCardInfo("Souldrinker", 158, Rarity.UNCOMMON, mage.cards.s.Souldrinker.class)); + cards.add(new SetCardInfo("Spell Blast", 89, Rarity.COMMON, mage.cards.s.SpellBlast.class)); + cards.add(new SetCardInfo("Spike Drone", 258, Rarity.COMMON, mage.cards.s.SpikeDrone.class)); + cards.add(new SetCardInfo("Spinal Graft", 159, Rarity.COMMON, mage.cards.s.SpinalGraft.class)); + cards.add(new SetCardInfo("Spirit Mirror", 48, Rarity.RARE, mage.cards.s.SpiritMirror.class)); + cards.add(new SetCardInfo("Spontaneous Combustion", 273, Rarity.UNCOMMON, mage.cards.s.SpontaneousCombustion.class)); + cards.add(new SetCardInfo("Squee's Toy", 309, Rarity.COMMON, mage.cards.s.SqueesToy.class)); + cards.add(new SetCardInfo("Stalking Stones", 327, Rarity.UNCOMMON, mage.cards.s.StalkingStones.class)); + cards.add(new SetCardInfo("Starke of Rath", 205, Rarity.RARE, mage.cards.s.StarkeOfRath.class)); + cards.add(new SetCardInfo("Static Orb", 310, Rarity.RARE, mage.cards.s.StaticOrb.class)); + cards.add(new SetCardInfo("Staunch Defenders", 49, Rarity.UNCOMMON, mage.cards.s.StaunchDefenders.class)); + cards.add(new SetCardInfo("Steal Enchantment", 90, Rarity.UNCOMMON, mage.cards.s.StealEnchantment.class)); + cards.add(new SetCardInfo("Stinging Licid", 91, Rarity.UNCOMMON, mage.cards.s.StingingLicid.class)); + cards.add(new SetCardInfo("Stone Rain", 206, Rarity.COMMON, mage.cards.s.StoneRain.class)); + cards.add(new SetCardInfo("Storm Front", 259, Rarity.UNCOMMON, mage.cards.s.StormFront.class)); + cards.add(new SetCardInfo("Stun", 207, Rarity.COMMON, mage.cards.s.Stun.class)); + cards.add(new SetCardInfo("Sudden Impact", 208, Rarity.UNCOMMON, mage.cards.s.SuddenImpact.class)); + cards.add(new SetCardInfo("Swamp", 339, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 340, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 341, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 342, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tahngarth's Rage", 209, Rarity.UNCOMMON, mage.cards.t.TahngarthsRage.class)); + cards.add(new SetCardInfo("Talon Sliver", 50, Rarity.COMMON, mage.cards.t.TalonSliver.class)); + cards.add(new SetCardInfo("Telethopter", 311, Rarity.UNCOMMON, mage.cards.t.Telethopter.class)); + cards.add(new SetCardInfo("Thalakos Dreamsower", 92, Rarity.UNCOMMON, mage.cards.t.ThalakosDreamsower.class)); + cards.add(new SetCardInfo("Thalakos Lowlands", 328, Rarity.UNCOMMON, mage.cards.t.ThalakosLowlands.class)); + cards.add(new SetCardInfo("Thalakos Mistfolk", 93, Rarity.COMMON, mage.cards.t.ThalakosMistfolk.class)); + cards.add(new SetCardInfo("Thalakos Seer", 94, Rarity.COMMON, mage.cards.t.ThalakosSeer.class)); + cards.add(new SetCardInfo("Thalakos Sentry", 95, Rarity.COMMON, mage.cards.t.ThalakosSentry.class)); + cards.add(new SetCardInfo("Thumbscrews", 312, Rarity.RARE, mage.cards.t.Thumbscrews.class)); + cards.add(new SetCardInfo("Time Ebb", 96, Rarity.COMMON, mage.cards.t.TimeEbb.class)); + cards.add(new SetCardInfo("Time Warp", 97, Rarity.RARE, mage.cards.t.TimeWarp.class)); + cards.add(new SetCardInfo("Tooth and Claw", 210, Rarity.RARE, mage.cards.t.ToothAndClaw.class)); + cards.add(new SetCardInfo("Torture Chamber", 313, Rarity.RARE, mage.cards.t.TortureChamber.class)); + cards.add(new SetCardInfo("Tradewind Rider", 98, Rarity.RARE, mage.cards.t.TradewindRider.class)); + cards.add(new SetCardInfo("Trained Armodon", 260, Rarity.COMMON, mage.cards.t.TrainedArmodon.class)); + cards.add(new SetCardInfo("Tranquility", 261, Rarity.COMMON, mage.cards.t.Tranquility.class)); + cards.add(new SetCardInfo("Trumpeting Armodon", 262, Rarity.UNCOMMON, mage.cards.t.TrumpetingArmodon.class)); + cards.add(new SetCardInfo("Twitch", 99, Rarity.COMMON, mage.cards.t.Twitch.class)); + cards.add(new SetCardInfo("Unstable Shapeshifter", 100, Rarity.RARE, mage.cards.u.UnstableShapeshifter.class)); + cards.add(new SetCardInfo("Vec Townships", 329, Rarity.UNCOMMON, mage.cards.v.VecTownships.class)); + cards.add(new SetCardInfo("Verdant Force", 263, Rarity.RARE, mage.cards.v.VerdantForce.class)); + cards.add(new SetCardInfo("Verdigris", 264, Rarity.UNCOMMON, mage.cards.v.Verdigris.class)); + cards.add(new SetCardInfo("Vhati il-Dal", 274, Rarity.RARE, mage.cards.v.VhatiIlDal.class)); + cards.add(new SetCardInfo("Volrath's Curse", 101, Rarity.COMMON, mage.cards.v.VolrathsCurse.class)); + cards.add(new SetCardInfo("Wall of Diffusion", 211, Rarity.COMMON, mage.cards.w.WallOfDiffusion.class)); + cards.add(new SetCardInfo("Warmth", 51, Rarity.UNCOMMON, mage.cards.w.Warmth.class)); + cards.add(new SetCardInfo("Wasteland", 330, Rarity.UNCOMMON, mage.cards.w.Wasteland.class)); + cards.add(new SetCardInfo("Watchdog", 314, Rarity.UNCOMMON, mage.cards.w.Watchdog.class)); + cards.add(new SetCardInfo("Whispers of the Muse", 103, Rarity.UNCOMMON, mage.cards.w.WhispersOfTheMuse.class)); + cards.add(new SetCardInfo("Wild Wurm", 212, Rarity.UNCOMMON, mage.cards.w.WildWurm.class)); + cards.add(new SetCardInfo("Wind Dancer", 104, Rarity.UNCOMMON, mage.cards.w.WindDancer.class)); + cards.add(new SetCardInfo("Wind Drake", 105, Rarity.COMMON, mage.cards.w.WindDrake.class)); + cards.add(new SetCardInfo("Winds of Rath", 52, Rarity.RARE, mage.cards.w.WindsOfRath.class)); + cards.add(new SetCardInfo("Winged Sliver", 106, Rarity.COMMON, mage.cards.w.WingedSliver.class)); + cards.add(new SetCardInfo("Winter's Grasp", 265, Rarity.UNCOMMON, mage.cards.w.WintersGrasp.class)); + cards.add(new SetCardInfo("Wood Sage", 275, Rarity.RARE, mage.cards.w.WoodSage.class)); + cards.add(new SetCardInfo("Worthy Cause", 53, Rarity.UNCOMMON, mage.cards.w.WorthyCause.class)); + } +} From c24077a5fb6930585a707734d4e443a1f65e981a Mon Sep 17 00:00:00 2001 From: L_J Date: Mon, 26 Nov 2018 12:47:41 +0000 Subject: [PATCH 48/61] Goblin Machinist fix Card didn't reveal the nonland card and put it on the bottom of library --- Mage.Sets/src/mage/cards/g/GoblinMachinist.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GoblinMachinist.java b/Mage.Sets/src/mage/cards/g/GoblinMachinist.java index 2e217d109bf..8ff675b00e9 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinMachinist.java +++ b/Mage.Sets/src/mage/cards/g/GoblinMachinist.java @@ -77,9 +77,9 @@ class GoblinMachinistEffect extends OneShotEffect { break; } } - controller.revealCards(source, cards, game); - controller.putCardsOnBottomOfLibrary(cards, game, source, true); } + controller.revealCards(source, cards, game); + controller.putCardsOnBottomOfLibrary(cards, game, source, true); return true; } return false; From 6e2dde92aabf42da3178f584c73e9d08a651826e Mon Sep 17 00:00:00 2001 From: L_J Date: Mon, 26 Nov 2018 14:30:17 +0000 Subject: [PATCH 49/61] Implemented Canal Dredger --- Mage.Sets/src/mage/cards/c/CanalDredger.java | 52 ++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/CanalDredger.java diff --git a/Mage.Sets/src/mage/cards/c/CanalDredger.java b/Mage.Sets/src/mage/cards/c/CanalDredger.java new file mode 100644 index 00000000000..d1a8d2b7bbe --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CanalDredger.java @@ -0,0 +1,52 @@ + +package mage.cards.c; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.target.common.TargetCardInYourGraveyard; + +/** + * + * @author L_J + */ +public final class CanalDredger extends CardImpl { + + public CanalDredger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}"); + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(1); + this.toughness = new MageInt(5); + + // TODO: Draft specific abilities not implemented + // Draft Canal Dredger face up. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("Draft Canal Dredger face up."))); + + // Each player passes the last card from each booster pack to a player who drafted a card named Canal Dredger. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("Each player passes the last card from each booster pack to a player who drafted a card named Canal Dredger."))); + + // {T}: Put target card from your graveyard on the bottom of your library. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutOnLibraryTargetEffect(false), new TapSourceCost()); + ability.addTarget(new TargetCardInYourGraveyard()); + this.addAbility(ability); + } + + public CanalDredger(final CanalDredger card) { + super(card); + } + + @Override + public CanalDredger copy() { + return new CanalDredger(this); + } +} From 3fd738b5e5a2eb6ab92984013db9108940b0af8a Mon Sep 17 00:00:00 2001 From: L_J Date: Mon, 26 Nov 2018 14:30:58 +0000 Subject: [PATCH 50/61] Implemented Deal Broker --- Mage.Sets/src/mage/cards/d/DealBroker.java | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/d/DealBroker.java diff --git a/Mage.Sets/src/mage/cards/d/DealBroker.java b/Mage.Sets/src/mage/cards/d/DealBroker.java new file mode 100644 index 00000000000..432f5836934 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DealBroker.java @@ -0,0 +1,50 @@ + +package mage.cards.d; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; + +/** + * + * @author L_J + */ +public final class DealBroker extends CardImpl { + + public DealBroker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT, CardType.CREATURE},"{3}"); + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // TODO: Draft specific abilities not implemented + // Draft Deal Broker face up. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("Draft Deal Broker face up."))); + + // Immediately after the draft, you may reveal a card in your card pool. Each other player may offer you one card in their card pool in exchange. You may accept any one offer. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new InfoEffect("Immediately after the draft, you may reveal a card in your card pool. " + + "Each other player may offer you one card in their card pool in exchange. You may accept any one offer."))); + + // {T}: Draw a card, then discard a card. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawDiscardControllerEffect(), new TapSourceCost())); + } + + public DealBroker(final DealBroker card) { + super(card); + } + + @Override + public DealBroker copy() { + return new DealBroker(this); + } + +} From 4844d439dbabe4e57357983aa4a30e0a7dd2f9c0 Mon Sep 17 00:00:00 2001 From: L_J Date: Mon, 26 Nov 2018 14:31:09 +0000 Subject: [PATCH 51/61] Implemented Canal Dredger and Deal Broker --- Mage.Sets/src/mage/sets/Conspiracy.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage.Sets/src/mage/sets/Conspiracy.java b/Mage.Sets/src/mage/sets/Conspiracy.java index 3e561263f15..bc992d21361 100644 --- a/Mage.Sets/src/mage/sets/Conspiracy.java +++ b/Mage.Sets/src/mage/sets/Conspiracy.java @@ -43,6 +43,7 @@ public final class Conspiracy extends ExpansionSet { cards.add(new SetCardInfo("Brainstorm", 91, Rarity.COMMON, mage.cards.b.Brainstorm.class)); cards.add(new SetCardInfo("Breakthrough", 92, Rarity.UNCOMMON, mage.cards.b.Breakthrough.class)); cards.add(new SetCardInfo("Brimstone Volley", 138, Rarity.COMMON, mage.cards.b.BrimstoneVolley.class)); + cards.add(new SetCardInfo("Canal Dredger", 55, Rarity.RARE, mage.cards.c.CanalDredger.class)); cards.add(new SetCardInfo("Charging Rhino", 159, Rarity.COMMON, mage.cards.c.ChargingRhino.class)); cards.add(new SetCardInfo("Chartooth Cougar", 139, Rarity.COMMON, mage.cards.c.ChartoothCougar.class)); cards.add(new SetCardInfo("Cinder Wall", 140, Rarity.COMMON, mage.cards.c.CinderWall.class)); @@ -57,6 +58,7 @@ public final class Conspiracy extends ExpansionSet { cards.add(new SetCardInfo("Custodi Squire", 18, Rarity.COMMON, mage.cards.c.CustodiSquire.class)); cards.add(new SetCardInfo("Dack Fayden", 42, Rarity.MYTHIC, mage.cards.d.DackFayden.class)); cards.add(new SetCardInfo("Dack's Duplicate", 43, Rarity.RARE, mage.cards.d.DacksDuplicate.class)); + cards.add(new SetCardInfo("Deal Broker", 61, Rarity.RARE, mage.cards.d.DealBroker.class)); cards.add(new SetCardInfo("Deathforge Shaman", 141, Rarity.UNCOMMON, mage.cards.d.DeathforgeShaman.class)); cards.add(new SetCardInfo("Deathreap Ritual", 44, Rarity.UNCOMMON, mage.cards.d.DeathreapRitual.class)); cards.add(new SetCardInfo("Deathrender", 197, Rarity.RARE, mage.cards.d.Deathrender.class)); From 126610b6a898dcc8ec8aac75ab64a0d556de0184 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 26 Nov 2018 16:16:59 +0100 Subject: [PATCH 52/61] Added handling of TargetCardInGraveyardOrBattlefield fpr TestPlayer. --- .../java/org/mage/test/player/TestPlayer.java | 478 +++++++++++++----- 1 file changed, 344 insertions(+), 134 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index ae5cdf3ca3f..46f890f9e27 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -1,5 +1,9 @@ package org.mage.test.player; +import java.io.Serializable; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import mage.MageObject; import mage.MageObjectReference; import mage.ObjectColor; @@ -43,19 +47,12 @@ import mage.player.ai.ComputerPlayer; import mage.players.Library; import mage.players.ManaPool; import mage.players.Player; -import mage.players.PlayerList; import mage.players.net.UserData; import mage.target.*; import mage.target.common.*; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Ignore; - -import java.io.Serializable; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*; /** @@ -132,7 +129,7 @@ public class TestPlayer implements Player { /** * @param maxCallsWithoutAction max number of priority passes a player may - * have for this test (default = 100) + * have for this test (default = 100) */ public void setMaxCallsWithoutAction(int maxCallsWithoutAction) { this.maxCallsWithoutAction = maxCallsWithoutAction; @@ -807,7 +804,7 @@ public class TestPlayer implements Player { // Loop through players and validate can attack/block this turn UUID defenderId = null; //List - for (Iterator it = actions.iterator(); it.hasNext(); ) { + for (Iterator it = actions.iterator(); it.hasNext();) { PlayerAction action = it.next(); if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) { String command = action.getAction(); @@ -1137,6 +1134,62 @@ public class TestPlayer implements Player { if (target.getTargetController() != null && target.getAbilityController() != null) { abilityControllerId = target.getAbilityController(); } + if (target instanceof TargetCardInGraveyardOrBattlefield) { + boolean removeTargetDefinition = false; + Filter filter = target.getFilter(); + for (String targetDefinition : targets) { + String[] targetList = targetDefinition.split("\\^"); + boolean targetFound = false; + for (String targetName : targetList) { + boolean originOnly = false; + boolean copyOnly = false; + if (targetName.endsWith("]")) { + if (targetName.endsWith("[no copy]")) { + originOnly = true; + targetName = targetName.substring(0, targetName.length() - 9); + } + if (targetName.endsWith("[only copy]")) { + copyOnly = true; + targetName = targetName.substring(0, targetName.length() - 11); + } + } + for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { + if (permanent.getName().equals(targetName) || (permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { + if (filter.match(permanent, game) && target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) { + if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { + target.add(permanent.getId(), game); + targetFound = true; + break; + } + } + } + } + if (!targetFound) { + for (UUID playerId : game.getState().getPlayersInRange(this.getId(), game)) { + Player player = game.getPlayer(playerId); + for (Card card : player.getGraveyard().getCards(((TargetCardInGraveyardOrBattlefield) target).getFilter(), game)) { + if (card.getName().equals(targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { + if (((TargetCardInYourGraveyard) target).canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) { + target.add(card.getId(), game); + targetFound = true; + break; + } + } + } + } + } + if (targetFound) { + targetFound = false; + removeTargetDefinition = true; + } + + } + if (removeTargetDefinition) { + targets.remove(targetDefinition); + return true; + } + } + } if (target instanceof TargetPlayer || target instanceof TargetAnyTarget || target instanceof TargetCreatureOrPlayer @@ -1304,7 +1357,10 @@ public class TestPlayer implements Player { } @Override - public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) { + public boolean chooseTarget(Outcome outcome, Cards cards, + TargetCard target, Ability source, + Game game + ) { if (!targets.isEmpty()) { for (String targetDefinition : targets) { String[] targetList = targetDefinition.split("\\^"); @@ -1328,7 +1384,8 @@ public class TestPlayer implements Player { } @Override - public TriggeredAbility chooseTriggeredAbility(List abilities, Game game) { + public TriggeredAbility chooseTriggeredAbility(List abilities, Game game + ) { if (!choices.isEmpty()) { for (TriggeredAbility ability : abilities) { if (ability.toString().startsWith(choices.get(0))) { @@ -1341,12 +1398,18 @@ public class TestPlayer implements Player { } @Override - public boolean chooseUse(Outcome outcome, String message, Ability source, Game game) { + public boolean chooseUse(Outcome outcome, String message, + Ability source, Game game + ) { return this.chooseUse(outcome, message, null, null, null, source, game); } @Override - public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) { + public boolean chooseUse(Outcome outcome, String message, + String secondMessage, String trueText, + String falseText, Ability source, + Game game + ) { if (message.equals("Scry 1?")) { return false; } @@ -1364,7 +1427,9 @@ public class TestPlayer implements Player { } @Override - public int announceXMana(int min, int max, String message, Game game, Ability ability) { + public int announceXMana(int min, int max, String message, + Game game, Ability ability + ) { if (!choices.isEmpty()) { for (String choice : choices) { if (choice.startsWith("X=")) { @@ -1378,7 +1443,10 @@ public class TestPlayer implements Player { } @Override - public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variablCost) { + public int announceXCost(int min, int max, String message, + Game game, Ability ability, + VariableCost variablCost + ) { if (!choices.isEmpty()) { if (choices.get(0).startsWith("X=")) { int xValue = Integer.parseInt(choices.get(0).substring(2)); @@ -1390,7 +1458,9 @@ public class TestPlayer implements Player { } @Override - public int getAmount(int min, int max, String message, Game game) { + public int getAmount(int min, int max, String message, + Game game + ) { if (!choices.isEmpty()) { if (choices.get(0).startsWith("X=")) { int xValue = Integer.parseInt(choices.get(0).substring(2)); @@ -1402,12 +1472,14 @@ public class TestPlayer implements Player { } @Override - public void addAbility(Ability ability) { + public void addAbility(Ability ability + ) { computerPlayer.addAbility(ability); } @Override - public boolean activateAbility(ActivatedAbility ability, Game game) { + public boolean activateAbility(ActivatedAbility ability, Game game + ) { return computerPlayer.activateAbility(ability, game); } @@ -1427,12 +1499,14 @@ public class TestPlayer implements Player { } @Override - public void won(Game game) { + public void won(Game game + ) { computerPlayer.won(game); } @Override - public void restore(Player player) { + public void restore(Player player + ) { this.modesSet.clear(); this.modesSet.addAll(((TestPlayer) player).modesSet); this.actions.clear(); @@ -1445,18 +1519,21 @@ public class TestPlayer implements Player { } @Override - public void useDeck(Deck deck, Game game) { + public void useDeck(Deck deck, Game game + ) { computerPlayer.useDeck(deck, game); } @Override - public void init(Game game) { + public void init(Game game + ) { initialTurns = 0; computerPlayer.init(game); } @Override - public void init(Game game, boolean testMode) { + public void init(Game game, boolean testMode + ) { initialTurns = 0; computerPlayer.init(game, testMode); } @@ -1472,12 +1549,14 @@ public class TestPlayer implements Player { } @Override - public void otherPlayerLeftGame(Game game) { + public void otherPlayerLeftGame(Game game + ) { computerPlayer.otherPlayerLeftGame(game); } @Override - public void beginTurn(Game game) { + public void beginTurn(Game game + ) { checkLegalMovesThisTurn(game); computerPlayer.beginTurn(game); } @@ -1498,12 +1577,14 @@ public class TestPlayer implements Player { } @Override - public void controlPlayersTurn(Game game, UUID playerId) { + public void controlPlayersTurn(Game game, UUID playerId + ) { computerPlayer.controlPlayersTurn(game, playerId); } @Override - public void setTurnControlledBy(UUID playerId) { + public void setTurnControlledBy(UUID playerId + ) { computerPlayer.setTurnControlledBy(playerId); } @@ -1523,77 +1604,98 @@ public class TestPlayer implements Player { } @Override - public void setGameUnderYourControl(boolean value) { + public void setGameUnderYourControl(boolean value + ) { computerPlayer.setGameUnderYourControl(value); } @Override - public void setGameUnderYourControl(boolean value, boolean fullRestore) { + public void setGameUnderYourControl(boolean value, boolean fullRestore + ) { computerPlayer.setGameUnderYourControl(value, fullRestore); } @Override - public void endOfTurn(Game game) { + public void endOfTurn(Game game + ) { computerPlayer.endOfTurn(game); } @Override - public boolean canBeTargetedBy(MageObject source, UUID sourceControllerId, Game game) { + public boolean canBeTargetedBy(MageObject source, UUID sourceControllerId, + Game game + ) { return computerPlayer.canBeTargetedBy(source, sourceControllerId, game); } @Override - public boolean hasProtectionFrom(MageObject source, Game game) { + public boolean hasProtectionFrom(MageObject source, Game game + ) { return computerPlayer.hasProtectionFrom(source, game); } @Override - public int drawCards(int num, Game game) { + public int drawCards(int num, Game game + ) { return computerPlayer.drawCards(num, game); } @Override - public int drawCards(int num, Game game, List appliedEffects) { + public int drawCards(int num, Game game, + List appliedEffects + ) { return computerPlayer.drawCards(num, game, appliedEffects); } @Override - public void discardToMax(Game game) { + public void discardToMax(Game game + ) { computerPlayer.discardToMax(game); } @Override - public boolean putInHand(Card card, Game game) { + public boolean putInHand(Card card, Game game + ) { return computerPlayer.putInHand(card, game); } @Override - public boolean removeFromHand(Card card, Game game) { + public boolean removeFromHand(Card card, Game game + ) { return computerPlayer.removeFromHand(card, game); } @Override - public boolean removeFromLibrary(Card card, Game game) { + public boolean removeFromLibrary(Card card, Game game + ) { return computerPlayer.removeFromLibrary(card, game); } @Override - public void discard(int amount, Ability source, Game game) { + public void discard(int amount, Ability source, + Game game + ) { computerPlayer.discard(amount, source, game); } @Override - public Card discardOne(boolean random, Ability source, Game game) { + public Card discardOne(boolean random, Ability source, + Game game + ) { return computerPlayer.discardOne(random, source, game); } @Override - public Cards discard(int amount, boolean random, Ability source, Game game) { + public Cards discard(int amount, boolean random, Ability source, + Game game + ) { return computerPlayer.discard(amount, random, source, game); } @Override - public boolean discard(Card card, Ability source, Game game) { + public boolean discard(Card card, Ability source, + Game game + ) { return computerPlayer.discard(card, source, game); } @@ -1603,52 +1705,67 @@ public class TestPlayer implements Player { } @Override - public boolean addAttachment(UUID permanentId, Game game) { + public boolean addAttachment(UUID permanentId, Game game + ) { return computerPlayer.addAttachment(permanentId, game); } @Override - public boolean removeAttachment(Permanent attachment, Game game) { + public boolean removeAttachment(Permanent attachment, Game game + ) { return computerPlayer.removeAttachment(attachment, game); } @Override - public boolean removeFromBattlefield(Permanent permanent, Game game) { + public boolean removeFromBattlefield(Permanent permanent, Game game + ) { return computerPlayer.removeFromBattlefield(permanent, game); } @Override - public boolean putInGraveyard(Card card, Game game) { + public boolean putInGraveyard(Card card, Game game + ) { return computerPlayer.putInGraveyard(card, game); } @Override - public boolean removeFromGraveyard(Card card, Game game) { + public boolean removeFromGraveyard(Card card, Game game + ) { return computerPlayer.removeFromGraveyard(card, game); } @Override - public boolean putCardsOnBottomOfLibrary(Card card, Game game, Ability source, boolean anyOrder) { + public boolean putCardsOnBottomOfLibrary(Card card, Game game, + Ability source, boolean anyOrder + ) { return computerPlayer.putCardsOnBottomOfLibrary(card, game, source, anyOrder); } @Override - public boolean putCardsOnBottomOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder) { + public boolean putCardsOnBottomOfLibrary(Cards cards, Game game, + Ability source, boolean anyOrder + ) { return computerPlayer.putCardsOnBottomOfLibrary(cards, game, source, anyOrder); } @Override - public boolean putCardOnTopXOfLibrary(Card card, Game game, Ability source, int xFromTheTop) { + public boolean putCardOnTopXOfLibrary(Card card, Game game, + Ability source, int xFromTheTop + ) { return computerPlayer.putCardOnTopXOfLibrary(card, game, source, xFromTheTop); } @Override - public boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder) { + public boolean putCardsOnTopOfLibrary(Cards cards, Game game, + Ability source, boolean anyOrder + ) { return computerPlayer.putCardsOnTopOfLibrary(cards, game, source, anyOrder); } @Override - public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts manaCosts, Costs costs) { + public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts manaCosts, + Costs costs + ) { computerPlayer.setCastSourceIdWithAlternateMana(sourceId, manaCosts, costs); } @@ -1673,27 +1790,36 @@ public class TestPlayer implements Player { } @Override - public boolean cast(SpellAbility ability, Game game, boolean noMana, MageObjectReference reference) { + public boolean cast(SpellAbility ability, Game game, + boolean noMana, MageObjectReference reference + ) { return computerPlayer.cast(ability, game, noMana, reference); } @Override - public boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference reference) { + public boolean playCard(Card card, Game game, + boolean noMana, boolean ignoreTiming, MageObjectReference reference + ) { return computerPlayer.playCard(card, game, noMana, ignoreTiming, reference); } @Override - public boolean playLand(Card card, Game game, boolean ignoreTiming) { + public boolean playLand(Card card, Game game, + boolean ignoreTiming + ) { return computerPlayer.playLand(card, game, ignoreTiming); } @Override - public boolean triggerAbility(TriggeredAbility source, Game game) { + public boolean triggerAbility(TriggeredAbility source, Game game + ) { return computerPlayer.triggerAbility(source, game); } @Override - public LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game) { + public LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, + Game game + ) { return computerPlayer.getUseableActivatedAbilities(object, zone, game); } @@ -1708,57 +1834,77 @@ public class TestPlayer implements Player { } @Override - public void shuffleLibrary(Ability source, Game game) { + public void shuffleLibrary(Ability source, Game game + ) { computerPlayer.shuffleLibrary(source, game); } @Override - public void revealCards(Ability source, Cards cards, Game game) { + public void revealCards(Ability source, Cards cards, + Game game + ) { computerPlayer.revealCards(source, cards, game); } @Override - public void revealCards(String name, Cards cards, Game game) { + public void revealCards(String name, Cards cards, + Game game + ) { computerPlayer.revealCards(name, cards, game); } @Override - public void revealCards(Ability source, String name, Cards cards, Game game) { + public void revealCards(Ability source, String name, + Cards cards, Game game + ) { computerPlayer.revealCards(name, cards, game); } @Override - public void revealCards(String name, Cards cards, Game game, boolean postToLog) { + public void revealCards(String name, Cards cards, + Game game, boolean postToLog + ) { computerPlayer.revealCards(name, cards, game, postToLog); } @Override - public void revealCards(Ability source, String name, Cards cards, Game game, boolean postToLog) { + public void revealCards(Ability source, String name, + Cards cards, Game game, + boolean postToLog + ) { computerPlayer.revealCards(name, cards, game, postToLog); } @Override - public void lookAtCards(String name, Cards cards, Game game) { + public void lookAtCards(String name, Cards cards, + Game game + ) { computerPlayer.lookAtCards(name, cards, game); } @Override - public void lookAtCards(Ability source, String name, Cards cards, Game game) { + public void lookAtCards(Ability source, String name, + Cards cards, Game game + ) { computerPlayer.lookAtCards(source, name, cards, game); } @Override - public void lookAtCards(String name, Card card, Game game) { + public void lookAtCards(String name, Card card, + Game game + ) { computerPlayer.lookAtCards(name, card, game); } @Override - public void phasing(Game game) { + public void phasing(Game game + ) { computerPlayer.phasing(game); } @Override - public void untap(Game game) { + public void untap(Game game + ) { computerPlayer.untap(game); } @@ -1813,22 +1959,28 @@ public class TestPlayer implements Player { } @Override - public void initLife(int life) { + public void initLife(int life + ) { computerPlayer.initLife(life); } @Override - public void setLife(int life, Game game, UUID sourceId) { + public void setLife(int life, Game game, + UUID sourceId + ) { computerPlayer.setLife(life, game, sourceId); } @Override - public void setLife(int life, Game game, Ability source) { + public void setLife(int life, Game game, + Ability source + ) { computerPlayer.setLife(life, game, source); } @Override - public void setLifeTotalCanChange(boolean lifeTotalCanChange) { + public void setLifeTotalCanChange(boolean lifeTotalCanChange + ) { computerPlayer.setLifeTotalCanChange(lifeTotalCanChange); } @@ -1848,12 +2000,15 @@ public class TestPlayer implements Player { } @Override - public void setCanLoseLife(boolean canLoseLife) { + public void setCanLoseLife(boolean canLoseLife + ) { computerPlayer.setCanLoseLife(canLoseLife); } @Override - public int loseLife(int amount, Game game, boolean atCombat) { + public int loseLife(int amount, Game game, + boolean atCombat + ) { return computerPlayer.loseLife(amount, game, atCombat); } @@ -1863,37 +2018,49 @@ public class TestPlayer implements Player { } @Override - public void setCanGainLife(boolean canGainLife) { + public void setCanGainLife(boolean canGainLife + ) { computerPlayer.setCanGainLife(canGainLife); } @Override - public int gainLife(int amount, Game game, Ability source) { + public int gainLife(int amount, Game game, + Ability source + ) { return computerPlayer.gainLife(amount, game, source); } @Override - public int gainLife(int amount, Game game, UUID sourceId) { + public int gainLife(int amount, Game game, + UUID sourceId + ) { return computerPlayer.gainLife(amount, game, sourceId); } @Override - public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable) { + public int damage(int damage, UUID sourceId, + Game game, boolean combatDamage, boolean preventable + ) { return computerPlayer.damage(damage, sourceId, game, combatDamage, preventable); } @Override - public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List appliedEffects) { + public int damage(int damage, UUID sourceId, + Game game, boolean combatDamage, boolean preventable, List appliedEffects + ) { return computerPlayer.damage(damage, sourceId, game, combatDamage, preventable, appliedEffects); } @Override - public boolean addCounters(Counter counter, Game game) { + public boolean addCounters(Counter counter, Game game + ) { return computerPlayer.addCounters(counter, game); } @Override - public void removeCounters(String name, int amount, Ability source, Game game) { + public void removeCounters(String name, int amount, Ability source, + Game game + ) { computerPlayer.removeCounters(name, amount, source, game); } @@ -1908,7 +2075,8 @@ public class TestPlayer implements Player { } @Override - public void setLandsPerTurn(int landsPerTurn) { + public void setLandsPerTurn(int landsPerTurn + ) { computerPlayer.setLandsPerTurn(landsPerTurn); } @@ -1918,7 +2086,8 @@ public class TestPlayer implements Player { } @Override - public void setLoyaltyUsePerTurn(int loyaltyUsePerTurn) { + public void setLoyaltyUsePerTurn(int loyaltyUsePerTurn + ) { computerPlayer.setLoyaltyUsePerTurn(loyaltyUsePerTurn); } @@ -1928,12 +2097,14 @@ public class TestPlayer implements Player { } @Override - public void setMaxHandSize(int maxHandSize) { + public void setMaxHandSize(int maxHandSize + ) { computerPlayer.setMaxHandSize(maxHandSize); } @Override - public void setMaxAttackedBy(int maxAttackedBy) { + public void setMaxAttackedBy(int maxAttackedBy + ) { computerPlayer.setMaxAttackedBy(maxAttackedBy); } @@ -1943,27 +2114,32 @@ public class TestPlayer implements Player { } @Override - public void setResponseString(String responseString) { + public void setResponseString(String responseString + ) { computerPlayer.setResponseString(responseString); } @Override - public void setResponseManaType(UUID manaTypePlayerId, ManaType responseManaType) { + public void setResponseManaType(UUID manaTypePlayerId, ManaType responseManaType + ) { computerPlayer.setResponseManaType(manaTypePlayerId, responseManaType); } @Override - public void setResponseUUID(UUID responseUUID) { + public void setResponseUUID(UUID responseUUID + ) { computerPlayer.setResponseUUID(responseUUID); } @Override - public void setResponseBoolean(Boolean responseBoolean) { + public void setResponseBoolean(Boolean responseBoolean + ) { computerPlayer.setResponseBoolean(responseBoolean); } @Override - public void setResponseInteger(Integer responseInteger) { + public void setResponseInteger(Integer responseInteger + ) { computerPlayer.setResponseInteger(responseInteger); } @@ -1973,7 +2149,8 @@ public class TestPlayer implements Player { } @Override - public void pass(Game game) { + public void pass(Game game + ) { computerPlayer.pass(game); } @@ -1993,27 +2170,33 @@ public class TestPlayer implements Player { } @Override - public void quit(Game game) { + public void quit(Game game + ) { computerPlayer.quit(game); } @Override - public void timerTimeout(Game game) { + public void timerTimeout(Game game + ) { computerPlayer.timerTimeout(game); } @Override - public void idleTimeout(Game game) { + public void idleTimeout(Game game + ) { computerPlayer.idleTimeout(game); } @Override - public void concede(Game game) { + public void concede(Game game + ) { computerPlayer.concede(game); } @Override - public void sendPlayerAction(mage.constants.PlayerAction playerAction, Game game, Object data) { + public void sendPlayerAction(mage.constants.PlayerAction playerAction, Game game, + Object data + ) { computerPlayer.sendPlayerAction(playerAction, game, data); } @@ -2028,7 +2211,8 @@ public class TestPlayer implements Player { } @Override - public void lost(Game game) { + public void lost(Game game + ) { computerPlayer.lost(game); } @@ -2038,17 +2222,20 @@ public class TestPlayer implements Player { } @Override - public void drew(Game game) { + public void drew(Game game + ) { computerPlayer.drew(game); } @Override - public void lostForced(Game game) { + public void lostForced(Game game + ) { computerPlayer.lostForced(game); } @Override - public boolean canLose(Game game) { + public boolean canLose(Game game + ) { return computerPlayer.canLose(game); } @@ -2073,77 +2260,100 @@ public class TestPlayer implements Player { } @Override - public void declareAttacker(UUID attackerId, UUID defenderId, Game game, boolean allowUndo) { + public void declareAttacker(UUID attackerId, UUID defenderId, + Game game, boolean allowUndo + ) { computerPlayer.declareAttacker(attackerId, defenderId, game, allowUndo); } @Override - public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game) { + public void declareBlocker(UUID defenderId, UUID blockerId, + UUID attackerId, Game game + ) { computerPlayer.declareBlocker(defenderId, blockerId, attackerId, game); } @Override - public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game, boolean allowUndo) { + public void declareBlocker(UUID defenderId, UUID blockerId, + UUID attackerId, Game game, + boolean allowUndo + ) { computerPlayer.declareBlocker(defenderId, blockerId, attackerId, game, allowUndo); } @Override - public boolean searchLibrary(TargetCardInLibrary target, Game game) { + public boolean searchLibrary(TargetCardInLibrary target, Game game + ) { return computerPlayer.searchLibrary(target, game); } @Override - public boolean searchLibrary(TargetCardInLibrary target, Game game, boolean triggerEvents) { + public boolean searchLibrary(TargetCardInLibrary target, Game game, + boolean triggerEvents + ) { return computerPlayer.searchLibrary(target, game, triggerEvents); } @Override - public boolean searchLibrary(TargetCardInLibrary target, Game game, UUID targetPlayerId) { + public boolean searchLibrary(TargetCardInLibrary target, Game game, + UUID targetPlayerId + ) { return computerPlayer.searchLibrary(target, game, targetPlayerId); } @Override - public boolean searchLibrary(TargetCardInLibrary target, Game game, UUID targetPlayerId, boolean triggerEvents) { + public boolean searchLibrary(TargetCardInLibrary target, Game game, + UUID targetPlayerId, boolean triggerEvents + ) { return computerPlayer.searchLibrary(target, game, targetPlayerId, triggerEvents); } @Override - public boolean flipCoin(Game game) { + public boolean flipCoin(Game game + ) { return computerPlayer.flipCoin(game); } @Override - public boolean flipCoin(Game game, ArrayList appliedEffects) { + public boolean flipCoin(Game game, ArrayList appliedEffects + ) { return computerPlayer.flipCoin(game, appliedEffects); } @Override - public int rollDice(Game game, int numSides) { + public int rollDice(Game game, int numSides + ) { return computerPlayer.rollDice(game, numSides); } @Override - public int rollDice(Game game, ArrayList appliedEffects, int numSides) { + public int rollDice(Game game, ArrayList appliedEffects, + int numSides + ) { return computerPlayer.rollDice(game, appliedEffects, numSides); } @Override - public List getAvailableAttackers(Game game) { + public List getAvailableAttackers(Game game + ) { return computerPlayer.getAvailableAttackers(game); } @Override - public List getAvailableAttackers(UUID defenderId, Game game) { + public List getAvailableAttackers(UUID defenderId, Game game + ) { return computerPlayer.getAvailableAttackers(defenderId, game); } @Override - public List getAvailableBlockers(Game game) { + public List getAvailableBlockers(Game game + ) { return computerPlayer.getAvailableBlockers(game); } @Override - public ManaOptions getManaAvailable(Game game) { + public ManaOptions getManaAvailable(Game game + ) { return computerPlayer.getManaAvailable(game); } @@ -2496,7 +2706,7 @@ public class TestPlayer implements Player { @Override public boolean choose(Outcome outcome, Target target, - UUID sourceId, Game game + UUID sourceId, Game game ) { // needed to call here the TestPlayer because it's overwitten return choose(outcome, target, sourceId, game, null); @@ -2504,7 +2714,7 @@ public class TestPlayer implements Player { @Override public boolean choose(Outcome outcome, Cards cards, - TargetCard target, Game game + TargetCard target, Game game ) { if (!choices.isEmpty()) { for (String choose2 : choices) { @@ -2536,7 +2746,7 @@ public class TestPlayer implements Player { @Override public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, - Ability source, Game game + Ability source, Game game ) { return computerPlayer.chooseTargetAmount(outcome, target, source, game); } @@ -2549,15 +2759,15 @@ public class TestPlayer implements Player { @Override public boolean choosePile(Outcome outcome, String message, - List pile1, List pile2, - Game game + List pile1, List pile2, + Game game ) { return computerPlayer.choosePile(outcome, message, pile1, pile2, game); } @Override public boolean playMana(Ability ability, ManaCost unpaid, - String promptText, Game game + String promptText, Game game ) { groupsForTargetHandling = null; return computerPlayer.playMana(ability, unpaid, promptText, game); @@ -2571,15 +2781,15 @@ public class TestPlayer implements Player { @Override public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, - List blockerOrder, Game game + List blockerOrder, Game game ) { return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); } @Override public void assignDamage(int damage, List targets, - String singleTargetName, UUID sourceId, - Game game + String singleTargetName, UUID sourceId, + Game game ) { computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game); } @@ -2598,14 +2808,14 @@ public class TestPlayer implements Player { @Override public void pickCard(List cards, Deck deck, - Draft draft + Draft draft ) { computerPlayer.pickCard(cards, deck, draft); } @Override public boolean scry(int value, Ability source, - Game game + Game game ) { // Don't scry at the start of the game. if (game.getTurnNum() == 1 && game.getStep() == null) { @@ -2616,44 +2826,44 @@ public class TestPlayer implements Player { @Override public boolean surveil(int value, Ability source, - Game game + Game game ) { return computerPlayer.surveil(value, source, game); } @Override public boolean moveCards(Card card, Zone toZone, - Ability source, Game game + Ability source, Game game ) { return computerPlayer.moveCards(card, toZone, source, game); } @Override public boolean moveCards(Card card, Zone toZone, - Ability source, Game game, - boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects ) { return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); } @Override public boolean moveCards(Cards cards, Zone toZone, - Ability source, Game game + Ability source, Game game ) { return computerPlayer.moveCards(cards, toZone, source, game); } @Override public boolean moveCards(Set cards, Zone toZone, - Ability source, Game game + Ability source, Game game ) { return computerPlayer.moveCards(cards, toZone, source, game); } @Override public boolean moveCards(Set cards, Zone toZone, - Ability source, Game game, - boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects + Ability source, Game game, + boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects ) { return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); } From 2323654a22dcfbafce4481620107771b97f2adc4 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 26 Nov 2018 19:00:59 +0100 Subject: [PATCH 53/61] Fixed Duplicity casting costs. --- Mage.Sets/src/mage/cards/d/Duplicity.java | 400 +++++++++--------- .../src/mage/cards/p/ProwlingPangolin.java | 5 +- 2 files changed, 202 insertions(+), 203 deletions(-) diff --git a/Mage.Sets/src/mage/cards/d/Duplicity.java b/Mage.Sets/src/mage/cards/d/Duplicity.java index ec2655933c0..42fd7d75ab4 100644 --- a/Mage.Sets/src/mage/cards/d/Duplicity.java +++ b/Mage.Sets/src/mage/cards/d/Duplicity.java @@ -1,200 +1,200 @@ -package mage.cards.d; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; -import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.discard.DiscardControllerEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.players.Player; -import mage.util.CardUtil; - -/** - * - * @author jeffwadsworth - */ -public final class Duplicity extends CardImpl { - - public Duplicity(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); - - // When Duplicity enters the battlefield, exile the top five cards of your library face down. - this.addAbility(new EntersBattlefieldTriggeredAbility(new DuplicityEffect(), false)); - - // At the beginning of your upkeep, you may exile all cards from your hand face down. If you do, put all other cards you own exiled with Duplicity into your hand. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DuplicityExileHandEffect(), TargetController.YOU, true)); - - // At the beginning of your end step, discard a card. - this.addAbility(new BeginningOfYourEndStepTriggeredAbility(new DiscardControllerEffect(1), false)); - - // When you lose control of Duplicity, put all cards exiled with Duplicity into their owner's graveyard. - this.addAbility(new LoseControlDuplicity()); - - } - - public Duplicity(final Duplicity card) { - super(card); - } - - @Override - public Duplicity copy() { - return new Duplicity(this); - } -} - -class DuplicityEffect extends OneShotEffect { - - public DuplicityEffect() { - super(Outcome.Exile); - staticText = "exile the top five cards of your library face down"; - } - - public DuplicityEffect(final DuplicityEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null - && sourceObject != null) { - if (controller.getLibrary().hasCards()) { - UUID exileId = CardUtil.getCardExileZoneId(game, source); - Set cardsToExile = controller.getLibrary().getTopCards(game, 5); - for (Card card : cardsToExile) { - controller.moveCardsToExile(card, source, game, true, exileId, sourceObject.getName()); - card.setFaceDown(true, game); - } - } - return true; - } - return false; - } - - @Override - public DuplicityEffect copy() { - return new DuplicityEffect(this); - } -} - -class DuplicityExileHandEffect extends OneShotEffect { - - public DuplicityExileHandEffect() { - super(Outcome.Exile); - staticText = "you may exile all cards from your hand face down. If you do, put all other cards you own exiled with {this} into your hand"; - } - - public DuplicityExileHandEffect(final DuplicityExileHandEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null - && sourceObject != null) { - if (!controller.getHand().isEmpty()) { - UUID exileId = CardUtil.getCardExileZoneId(game, source); - Set cardsFromHandToExile = controller.getHand().getCards(game); - for (Card card : cardsFromHandToExile) { - controller.moveCardsToExile(card, source, game, true, exileId, sourceObject.getName()); - card.setFaceDown(true, game); - } - Set cardsInExile = game.getExile().getExileZone(exileId).getCards(game); - Set cardsToReturnToHandFromExile = new HashSet<>(); - for (Card card : cardsInExile) { - if (!cardsFromHandToExile.contains(card)) { - cardsToReturnToHandFromExile.add(card); - } - } - controller.moveCards(cardsToReturnToHandFromExile, Zone.HAND, source, game); - } - return true; - } - return false; - } - - @Override - public DuplicityExileHandEffect copy() { - return new DuplicityExileHandEffect(this); - } -} - -class LoseControlDuplicity extends DelayedTriggeredAbility { - - public LoseControlDuplicity() { - super(new PutExiledCardsInOwnersGraveyard(), Duration.EndOfGame, false); - } - - public LoseControlDuplicity(final LoseControlDuplicity ability) { - super(ability); - } - - @Override - public LoseControlDuplicity copy() { - return new LoseControlDuplicity(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.LOST_CONTROL; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(controllerId); - } - - @Override - public String getRule() { - return "When you lose control of {this}, put all cards exiled with {this} into their owner's graveyard."; - } -} - -class PutExiledCardsInOwnersGraveyard extends OneShotEffect { - - public PutExiledCardsInOwnersGraveyard() { - super(Outcome.Neutral); - staticText = " put all cards exiled with {this} into their owner's graveyard."; - } - - public PutExiledCardsInOwnersGraveyard(final PutExiledCardsInOwnersGraveyard effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null - && sourceObject != null) { - UUID exileId = CardUtil.getCardExileZoneId(game, source); - Set cardsInExile = game.getExile().getExileZone(exileId).getCards(game); - controller.moveCardsToGraveyardWithInfo(cardsInExile, source, game, Zone.EXILED); - return true; - } - return false; - } - - @Override - public PutExiledCardsInOwnersGraveyard copy() { - return new PutExiledCardsInOwnersGraveyard(this); - } -} +package mage.cards.d; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.discard.DiscardControllerEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author jeffwadsworth + */ +public final class Duplicity extends CardImpl { + + public Duplicity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}"); + + // When Duplicity enters the battlefield, exile the top five cards of your library face down. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DuplicityEffect(), false)); + + // At the beginning of your upkeep, you may exile all cards from your hand face down. If you do, put all other cards you own exiled with Duplicity into your hand. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DuplicityExileHandEffect(), TargetController.YOU, true)); + + // At the beginning of your end step, discard a card. + this.addAbility(new BeginningOfYourEndStepTriggeredAbility(new DiscardControllerEffect(1), false)); + + // When you lose control of Duplicity, put all cards exiled with Duplicity into their owner's graveyard. + this.addAbility(new LoseControlDuplicity()); + + } + + public Duplicity(final Duplicity card) { + super(card); + } + + @Override + public Duplicity copy() { + return new Duplicity(this); + } +} + +class DuplicityEffect extends OneShotEffect { + + public DuplicityEffect() { + super(Outcome.Exile); + staticText = "exile the top five cards of your library face down"; + } + + public DuplicityEffect(final DuplicityEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null + && sourceObject != null) { + if (controller.getLibrary().hasCards()) { + UUID exileId = CardUtil.getCardExileZoneId(game, source); + Set cardsToExile = controller.getLibrary().getTopCards(game, 5); + for (Card card : cardsToExile) { + controller.moveCardsToExile(card, source, game, true, exileId, sourceObject.getName()); + card.setFaceDown(true, game); + } + } + return true; + } + return false; + } + + @Override + public DuplicityEffect copy() { + return new DuplicityEffect(this); + } +} + +class DuplicityExileHandEffect extends OneShotEffect { + + public DuplicityExileHandEffect() { + super(Outcome.Exile); + staticText = "you may exile all cards from your hand face down. If you do, put all other cards you own exiled with {this} into your hand"; + } + + public DuplicityExileHandEffect(final DuplicityExileHandEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null + && sourceObject != null) { + if (!controller.getHand().isEmpty()) { + UUID exileId = CardUtil.getCardExileZoneId(game, source); + Set cardsFromHandToExile = controller.getHand().getCards(game); + for (Card card : cardsFromHandToExile) { + controller.moveCardsToExile(card, source, game, true, exileId, sourceObject.getName()); + card.setFaceDown(true, game); + } + Set cardsInExile = game.getExile().getExileZone(exileId).getCards(game); + Set cardsToReturnToHandFromExile = new HashSet<>(); + for (Card card : cardsInExile) { + if (!cardsFromHandToExile.contains(card)) { + cardsToReturnToHandFromExile.add(card); + } + } + controller.moveCards(cardsToReturnToHandFromExile, Zone.HAND, source, game); + } + return true; + } + return false; + } + + @Override + public DuplicityExileHandEffect copy() { + return new DuplicityExileHandEffect(this); + } +} + +class LoseControlDuplicity extends DelayedTriggeredAbility { + + public LoseControlDuplicity() { + super(new PutExiledCardsInOwnersGraveyard(), Duration.EndOfGame, false); + } + + public LoseControlDuplicity(final LoseControlDuplicity ability) { + super(ability); + } + + @Override + public LoseControlDuplicity copy() { + return new LoseControlDuplicity(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LOST_CONTROL; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getPlayerId().equals(controllerId); + } + + @Override + public String getRule() { + return "When you lose control of {this}, put all cards exiled with {this} into their owner's graveyard."; + } +} + +class PutExiledCardsInOwnersGraveyard extends OneShotEffect { + + public PutExiledCardsInOwnersGraveyard() { + super(Outcome.Neutral); + staticText = " put all cards exiled with {this} into their owner's graveyard."; + } + + public PutExiledCardsInOwnersGraveyard(final PutExiledCardsInOwnersGraveyard effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null + && sourceObject != null) { + UUID exileId = CardUtil.getCardExileZoneId(game, source); + Set cardsInExile = game.getExile().getExileZone(exileId).getCards(game); + controller.moveCardsToGraveyardWithInfo(cardsInExile, source, game, Zone.EXILED); + return true; + } + return false; + } + + @Override + public PutExiledCardsInOwnersGraveyard copy() { + return new PutExiledCardsInOwnersGraveyard(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ProwlingPangolin.java b/Mage.Sets/src/mage/cards/p/ProwlingPangolin.java index a6c373ffd12..e6eed743932 100644 --- a/Mage.Sets/src/mage/cards/p/ProwlingPangolin.java +++ b/Mage.Sets/src/mage/cards/p/ProwlingPangolin.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -11,8 +10,8 @@ import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -26,7 +25,7 @@ import mage.target.common.TargetControlledPermanent; public final class ProwlingPangolin extends CardImpl { public ProwlingPangolin(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.BEAST, SubType.PANGOLIN); this.power = new MageInt(6); this.toughness = new MageInt(5); From b7ebff4fa1b423544211c2b8fe2bdc80a83b09d9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 26 Nov 2018 19:01:18 +0100 Subject: [PATCH 54/61] Fixed verify color check. --- .../java/mage/verify/VerifyCardDataTest.java | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index fb16264f15f..d170b83cd57 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -1,7 +1,18 @@ package mage.verify; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import mage.ObjectColor; -import mage.abilities.keyword.DevoidAbility; import mage.abilities.keyword.MultikickerAbility; import mage.cards.*; import mage.cards.basiclands.BasicLand; @@ -18,19 +29,6 @@ import org.mage.plugins.card.images.CardDownloadData; import org.mage.plugins.card.images.DownloadPictures; import org.reflections.Reflections; -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Modifier; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Paths; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - /** * @author JayDi85 */ @@ -108,7 +106,7 @@ public class VerifyCardDataTest { } private int failed = 0; - private ArrayList outputMessages = new ArrayList<>(); + private final ArrayList outputMessages = new ArrayList<>(); @Test public void verifyCards() throws IOException { @@ -353,7 +351,6 @@ public class VerifyCardDataTest { } // TODO: add test to check num cards for rarity (rarityStats > 0 and numRarity > 0) - printMessages(warningsList); printMessages(errorsList); if (errorsList.size() > 0) { @@ -368,7 +365,6 @@ public class VerifyCardDataTest { Collection sets = Sets.getInstance().values(); - // 1. wrong UsesVariousArt settings (set have duplicated card name without that setting -- e.g. cards will have same image) for (ExpansionSet set : sets) { @@ -572,18 +568,18 @@ public class VerifyCardDataTest { if (ref.colors != null) { expected.addAll(ref.colors); } - if(card.isFlipCard()){ + if (card.isFlipCard()) { expected.addAll(ref.colorIdentity); } ObjectColor color = card.getColor(null); if (expected.size() != color.getColorCount() - || (color.isBlack() && !expected.contains("B")) - || (color.isBlue() && !expected.contains("U")) - || (color.isGreen() && !expected.contains("G")) - || (color.isRed() && !expected.contains("R")) - || (color.isWhite() && !expected.contains("W"))) { + || (color.isBlack() && !expected.contains("Black")) + || (color.isBlue() && !expected.contains("Blue")) + || (color.isGreen() && !expected.contains("Green")) + || (color.isRed() && !expected.contains("Red")) + || (color.isWhite() && !expected.contains("White"))) { fail(card, "colors", color + " != " + expected); } } @@ -598,7 +594,7 @@ public class VerifyCardDataTest { // fix names (e.g. Urza’s to Urza's) if (expected != null && expected.contains("Urza’s")) { expected = new ArrayList<>(expected); - for (ListIterator it = ((List) expected).listIterator(); it.hasNext(); ) { + for (ListIterator it = ((List) expected).listIterator(); it.hasNext();) { if (it.next().equals("Urza’s")) { it.set("Urza's"); } From e6ce58d6bfe2c6375b7612f07d40da717a8bc29e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 26 Nov 2018 19:30:22 +0100 Subject: [PATCH 55/61] XMage 1.4.32V0 --- Mage.Client/pom.xml | 2 +- Mage.Common/pom.xml | 2 +- Mage.Common/src/main/java/mage/utils/MageVersion.java | 4 ++-- Mage.Plugins/Mage.Counter.Plugin/pom.xml | 2 +- Mage.Plugins/pom.xml | 2 +- Mage.Server.Console/pom.xml | 2 +- Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml | 2 +- Mage.Server.Plugins/Mage.Deck.Limited/pom.xml | 2 +- Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml | 2 +- Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml | 2 +- Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml | 2 +- Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml | 2 +- Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml | 2 +- Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml | 2 +- .../Mage.Game.FreeformCommanderFreeForAll/pom.xml | 2 +- Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml | 2 +- Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml | 2 +- .../Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml | 2 +- Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml | 2 +- Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml | 2 +- Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml | 2 +- Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml | 2 +- Mage.Server.Plugins/Mage.Player.AI/pom.xml | 2 +- Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml | 2 +- Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml | 2 +- Mage.Server.Plugins/Mage.Player.Human/pom.xml | 2 +- Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml | 2 +- Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml | 2 +- Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml | 2 +- Mage.Server.Plugins/pom.xml | 2 +- Mage.Server/pom.xml | 2 +- Mage.Sets/pom.xml | 2 +- Mage.Stats/pom.xml | 2 +- Mage.Tests/pom.xml | 2 +- Mage.Updater/pom.xml | 2 +- Mage.Verify/pom.xml | 4 ++-- Mage/pom.xml | 2 +- Mage/src/main/java/mage/cards/repository/CardRepository.java | 2 +- pom.xml | 4 ++-- 39 files changed, 42 insertions(+), 42 deletions(-) diff --git a/Mage.Client/pom.xml b/Mage.Client/pom.xml index 35baf24109c..893201b5c1e 100644 --- a/Mage.Client/pom.xml +++ b/Mage.Client/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.31 + 1.4.32 org.mage diff --git a/Mage.Common/pom.xml b/Mage.Common/pom.xml index a139eab9600..5eb7a110dab 100644 --- a/Mage.Common/pom.xml +++ b/Mage.Common/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.31 + 1.4.32 mage-common diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java index 4a0feb9728e..d6628b39493 100644 --- a/Mage.Common/src/main/java/mage/utils/MageVersion.java +++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java @@ -13,8 +13,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 = 31; - public final static String MAGE_VERSION_MINOR_PATCH = "V4"; + public final static int MAGE_VERSION_PATCH = 32; + public final static String MAGE_VERSION_MINOR_PATCH = "V0"; public final static String MAGE_VERSION_INFO = ""; private final int major; diff --git a/Mage.Plugins/Mage.Counter.Plugin/pom.xml b/Mage.Plugins/Mage.Counter.Plugin/pom.xml index a8af62ad9da..27f3f04daa8 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.31 + 1.4.32 mage-counter-plugin diff --git a/Mage.Plugins/pom.xml b/Mage.Plugins/pom.xml index 19abee5bda1..8ae91a6e66c 100644 --- a/Mage.Plugins/pom.xml +++ b/Mage.Plugins/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.31 + 1.4.32 mage-plugins diff --git a/Mage.Server.Console/pom.xml b/Mage.Server.Console/pom.xml index a48379e45c7..ecbbafcdb4c 100644 --- a/Mage.Server.Console/pom.xml +++ b/Mage.Server.Console/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.31 + 1.4.32 org.mage diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml b/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml index 34ac2c8b537..ae308a042c8 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.31 + 1.4.32 mage-deck-constructed diff --git a/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml b/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml index 831c92012e6..98e6f8d47fb 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.31 + 1.4.32 mage-deck-limited diff --git a/Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml index be720b6a91e..c8e41100ec6 100644 --- a/Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.BrawlDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.31 + 1.4.32 mage-game-brawlduel diff --git a/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml index 45666026043..6acd4acfc23 100644 --- a/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.BrawlFreeForAll/pom.xml @@ -6,7 +6,7 @@ org.mage mage-server-plugins - 1.4.31 + 1.4.32 mage-game-brawlfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.CanadianHighlanderDuel/pom.xml index 5b32376a59e..6a96d18e1ea 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.31 + 1.4.32 mage-game-canadianhighlanderduel diff --git a/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml index 3bc9525794c..6bdd993258d 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.31 + 1.4.32 mage-game-commanderduel diff --git a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml index 1888b3a1fb6..159cbc07010 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.31 + 1.4.32 mage-game-commanderfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml index 239c59dcdf3..9c8f09b34e8 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.31 + 1.4.32 mage-game-freeforall diff --git a/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeformCommanderFreeForAll/pom.xml index eaa55ae4e9c..3c49cfae83e 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.31 + 1.4.32 mage-game-freeformcommanderfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml index e4ec5b48a76..2c78b0d5ad6 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.31 + 1.4.32 mage-game-momirduel diff --git a/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml b/Mage.Server.Plugins/Mage.Game.MomirGame/pom.xml index e17bfe1289a..79f3c5abdf1 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.31 + 1.4.32 mage-game-momirfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.PennyDreadfulCommanderFreeForAll/pom.xml index 60538dc66c3..f110bd92500 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.31 + 1.4.32 mage-game-pennydreadfulcommanderfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml index 111fedc13fb..c0744d767fe 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.31 + 1.4.32 mage-game-tinyleadersduel diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml index 5d5ba7eb623..9f0fcd6fc9d 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.31 + 1.4.32 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 75c0e758224..6bf13c4d634 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.31 + 1.4.32 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 9f8a8b0e53d..f5503335224 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.31 + 1.4.32 mage-player-ai-ma diff --git a/Mage.Server.Plugins/Mage.Player.AI/pom.xml b/Mage.Server.Plugins/Mage.Player.AI/pom.xml index 81f988b7341..3d07accfddf 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.31 + 1.4.32 mage-player-ai diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml b/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml index d8a75c7fde3..c3c78bb4e93 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.31 + 1.4.32 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 1cc373afd2b..1a6103ef54d 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.31 + 1.4.32 mage-player-aiminimax diff --git a/Mage.Server.Plugins/Mage.Player.Human/pom.xml b/Mage.Server.Plugins/Mage.Player.Human/pom.xml index b295f64ad4d..59ecd9969b5 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.31 + 1.4.32 mage-player-human diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml index f3cecab6e5f..8e791631f4f 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.31 + 1.4.32 mage-tournament-boosterdraft diff --git a/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml b/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml index bec51f060af..91a6ef148f1 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.31 + 1.4.32 mage-tournament-constructed diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml b/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml index b9aab5d4f47..d51fc65a620 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.31 + 1.4.32 mage-tournament-sealed diff --git a/Mage.Server.Plugins/pom.xml b/Mage.Server.Plugins/pom.xml index a3ac123d038..6e660046e92 100644 --- a/Mage.Server.Plugins/pom.xml +++ b/Mage.Server.Plugins/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.31 + 1.4.32 mage-server-plugins diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index 91d840c5a8c..0f1f7462b2a 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.31 + 1.4.32 mage-server diff --git a/Mage.Sets/pom.xml b/Mage.Sets/pom.xml index f478419c232..6f88ade019a 100644 --- a/Mage.Sets/pom.xml +++ b/Mage.Sets/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.31 + 1.4.32 org.mage diff --git a/Mage.Stats/pom.xml b/Mage.Stats/pom.xml index 394db54aa1a..6d51c6b7024 100644 --- a/Mage.Stats/pom.xml +++ b/Mage.Stats/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.31 + 1.4.32 org.mage diff --git a/Mage.Tests/pom.xml b/Mage.Tests/pom.xml index 1f5e3496407..210ea3d16e4 100644 --- a/Mage.Tests/pom.xml +++ b/Mage.Tests/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.31 + 1.4.32 mage-tests diff --git a/Mage.Updater/pom.xml b/Mage.Updater/pom.xml index c9ab517c295..183b4f90e1e 100644 --- a/Mage.Updater/pom.xml +++ b/Mage.Updater/pom.xml @@ -5,7 +5,7 @@ mage-root org.mage - 1.4.31 + 1.4.32 4.0.0 diff --git a/Mage.Verify/pom.xml b/Mage.Verify/pom.xml index cb1f3be8672..fb561bf2371 100644 --- a/Mage.Verify/pom.xml +++ b/Mage.Verify/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.31 + 1.4.32 mage-verify @@ -49,7 +49,7 @@ org.mage mage-client - 1.4.31 + 1.4.32 diff --git a/Mage/pom.xml b/Mage/pom.xml index 5666f64aff8..ecb37107e1a 100644 --- a/Mage/pom.xml +++ b/Mage/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.31 + 1.4.32 mage diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java index 229b6351a23..c4516c3ccd2 100644 --- a/Mage/src/main/java/mage/cards/repository/CardRepository.java +++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java @@ -32,7 +32,7 @@ public enum CardRepository { // raise this if db structure was changed private static final long CARD_DB_VERSION = 51; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 122; + private static final long CARD_CONTENT_VERSION = 123; private Dao cardDao; private Set classNames; diff --git a/pom.xml b/pom.xml index 62c3c3a0d9a..ef3a97cfd4e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.31 + 1.4.32 pom Mage Root Mage Root POM @@ -84,7 +84,7 @@ - 1.4.31 + 1.4.32 UTF-8 From fbf2b5f7b6672652dc36899fb52fd7340fce2cc3 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 26 Nov 2018 15:34:07 -0600 Subject: [PATCH 56/61] - A few fixes for Jace, Architect of Thought. --- .../mage/cards/j/JaceArchitectOfThought.java | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java index fbf022f466b..4f6b074b34f 100644 --- a/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java +++ b/Mage.Sets/src/mage/cards/j/JaceArchitectOfThought.java @@ -1,4 +1,3 @@ - package mage.cards.j; import java.util.ArrayList; @@ -11,7 +10,6 @@ import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.Card; @@ -125,9 +123,9 @@ class JaceArchitectOfThoughtDelayedTriggeredAbility extends DelayedTriggeredAbil @Override public boolean checkTrigger(GameEvent event, Game game) { if (game.getOpponents(getControllerId()).contains(event.getPlayerId())) { - for (Effect effect : getEffects()) { + getEffects().forEach((effect) -> { effect.setTargetPointer(new FixedTarget(event.getSourceId())); - } + }); return true; } return false; @@ -186,7 +184,6 @@ class JaceArchitectOfThoughtEffect2 extends OneShotEffect { if (opponent == null) { opponent = game.getPlayer(opponents.iterator().next()); } - TargetCard target = new TargetCard(0, allCards.size(), Zone.LIBRARY, new FilterCard("cards to put in the first pile")); target.setNotTarget(true); opponent.choose(Outcome.Neutral, allCards, target, game); @@ -212,9 +209,9 @@ class JaceArchitectOfThoughtEffect2 extends OneShotEffect { private void postPileToLog(String pileName, Set cards, Game game) { StringBuilder message = new StringBuilder(pileName).append(": "); - for (Card card : cards) { + cards.forEach((card) -> { message.append(card.getName()).append(' '); - } + }); if (cards.isEmpty()) { message.append(" (empty)"); } @@ -255,9 +252,9 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { while (!playerList.isEmpty()) { FilterPlayer filter = new FilterPlayer(); List playerPredicates = new ArrayList<>(); - for (UUID playerId : playerList) { + playerList.forEach((playerId) -> { playerPredicates.add(new PlayerIdPredicate(playerId)); - } + }); filter.add(Predicates.or(playerPredicates)); TargetPlayer targetPlayer = new TargetPlayer(1, 1, true, filter); targetPlayer.setRequired(!checkList.containsAll(playerList)); @@ -275,7 +272,7 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { UUID targetId = target.getFirstTarget(); Card card = player.getLibrary().remove(targetId, game); if (card != null) { - controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true); + controller.moveCardsToExile(card, source, game, true, CardUtil.getCardExileZoneId(game, source), sourcePermanent.getName()); playerList.remove(playerId); } } else { @@ -287,26 +284,23 @@ class JaceArchitectOfThoughtEffect3 extends OneShotEffect { } else { break; } - for (UUID playerId : playerList) { - Player player = game.getPlayer(playerId); - if (player == null || !player.canRespond()) { - playerList.remove(player); - } - } - } - for (UUID playerId : checkList) { - Player player = game.getPlayer(playerId); - if (player != null) { - player.shuffleLibrary(source, game); - } + playerList.stream().map((playerId) -> game.getPlayer(playerId)).filter((player) -> (player == null + || !player.canRespond())).forEachOrdered((player) -> { + playerList.remove(player.getId()); + }); } + checkList.stream().map((playerId) -> game.getPlayer(playerId)).filter((player) -> (player != null)).forEachOrdered((player) -> { + player.shuffleLibrary(source, game); + }); ExileZone jaceExileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); if (jaceExileZone == null) { return true; } FilterCard filter = new FilterCard("card to cast without mana costs"); TargetCardInExile target = new TargetCardInExile(filter, source.getSourceId()); - while (jaceExileZone.count(filter, game) > 0 && controller.choose(Outcome.PlayForFree, jaceExileZone, target, game)) { + while (jaceExileZone.count(filter, game) > 0 + && controller.chooseUse(Outcome.Benefit, "Cast another spell from exile zone for free?", source, game)) { + controller.choose(Outcome.PlayForFree, jaceExileZone, target, game); Card card = game.getCard(target.getFirstTarget()); if (card != null) { if (controller.cast(card.getSpellAbility(), game, true, new MageObjectReference(source.getSourceObject(game), game))) { From 8addea6891188f9a8f1ec7d799f4f547c3f4d03e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 27 Nov 2018 00:29:47 +0100 Subject: [PATCH 57/61] Removed debug message. --- .../main/java/mage/abilities/meta/OrTriggeredAbility.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java b/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java index 1f0723298d2..6882a8886ca 100644 --- a/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java @@ -1,5 +1,8 @@ package mage.abilities.meta; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; @@ -8,10 +11,6 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.watchers.Watcher; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - /** * A triggered ability that combines several others and triggers whenever one or * more of them would. The abilities passed in should have null as their effect, @@ -56,7 +55,6 @@ public class OrTriggeredAbility extends TriggeredAbilityImpl { public boolean checkEventType(GameEvent event, Game game) { for (TriggeredAbility ability : triggeredAbilities) { if (ability.checkEventType(event, game)) { - System.out.println("Correct event type (" + event.getType() + ")"); return true; } } From 6c55ede2c252239075dafb3c4bdaee648b770b95 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 27 Nov 2018 00:37:48 +0100 Subject: [PATCH 58/61] Removed debug message. --- Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java b/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java index 6882a8886ca..0a4a17cb4c2 100644 --- a/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java @@ -67,11 +67,9 @@ public class OrTriggeredAbility extends TriggeredAbilityImpl { for (int i = 0; i < triggeredAbilities.length; i++) { TriggeredAbility ability = triggeredAbilities[i]; if (ability.checkEventType(event, game) && ability.checkTrigger(event, game)) { - System.out.println("Triggered from " + ability.getRule()); triggeringAbilities.add(i); toRet = true; } - System.out.println("Checked " + ability.getRule()); } return toRet; } From eb996716bc86288050ee445aff54da21931d9fb6 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 26 Nov 2018 17:57:15 -0600 Subject: [PATCH 59/61] - Added Walking Dream and Mogg Bombers. --- Mage.Sets/src/mage/cards/m/MoggBombers.java | 60 ++++ Mage.Sets/src/mage/cards/w/WalkingDream.java | 59 ++++ Mage.Sets/src/mage/sets/Stronghold.java | 334 ++++++++++--------- 3 files changed, 287 insertions(+), 166 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/m/MoggBombers.java create mode 100644 Mage.Sets/src/mage/cards/w/WalkingDream.java diff --git a/Mage.Sets/src/mage/cards/m/MoggBombers.java b/Mage.Sets/src/mage/cards/m/MoggBombers.java new file mode 100644 index 00000000000..65700c1643e --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoggBombers.java @@ -0,0 +1,60 @@ +package mage.cards.m; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.AnotherPredicate; +import mage.target.TargetPlayer; + +/** + * + * @author jeffwadsworth + */ +public final class MoggBombers extends CardImpl { + + private static final String rule = "When another creature enters the battlefield, sacrifice {this} and it deals 3 damage to target player."; + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature"); + + static { + filter.add(new AnotherPredicate()); + } + + public MoggBombers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // When another creature enters the battlefield, sacrifice Mogg Bombers and it deals 3 damage to target player. + Effect sacrificeMoggBombers = new SacrificeSourceEffect(); + Effect damageTargetPlayer = new DamageTargetEffect(3); + Ability ability = new EntersBattlefieldAllTriggeredAbility( + Zone.BATTLEFIELD, + sacrificeMoggBombers, + filter, false, rule); + ability.addEffect(damageTargetPlayer); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + + } + + public MoggBombers(final MoggBombers card) { + super(card); + } + + @Override + public MoggBombers copy() { + return new MoggBombers(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WalkingDream.java b/Mage.Sets/src/mage/cards/w/WalkingDream.java new file mode 100644 index 00000000000..30cacb3dae8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WalkingDream.java @@ -0,0 +1,59 @@ +package mage.cards.w; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.OpponentControlsPermanentCondition; +import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; +import mage.abilities.effects.ContinuousRuleModifyingEffect; +import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; +import mage.abilities.keyword.CantBeBlockedSourceAbility; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; + +/** + * + * @author jeffwadsworth + */ +public final class WalkingDream extends CardImpl { + + private static final String rule = "{this} doesn't untap during your untap step if an opponent controls two or more creatures."; + + public WalkingDream(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.ILLUSION); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Walking Dream is unblockable. + this.addAbility(new CantBeBlockedSourceAbility()); + + // Walking Dream doesn't untap during your untap step if an opponent controls two or more creatures. + ContinuousRuleModifyingEffect dontUntap = new DontUntapInControllersUntapStepSourceEffect(false, true); + dontUntap.setText(rule); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousRuleModifyingEffect( + dontUntap, + new OpponentControlsPermanentCondition( + new FilterCreaturePermanent(), + ComparisonType.MORE_THAN, 1))); + this.addAbility(ability); + + } + + public WalkingDream(final WalkingDream card) { + super(card); + } + + @Override + public WalkingDream copy() { + return new WalkingDream(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Stronghold.java b/Mage.Sets/src/mage/sets/Stronghold.java index 03ae248545b..0fe9bdb4cad 100644 --- a/Mage.Sets/src/mage/sets/Stronghold.java +++ b/Mage.Sets/src/mage/sets/Stronghold.java @@ -1,166 +1,168 @@ - -package mage.sets; - -import mage.cards.ExpansionSet; -import mage.constants.Rarity; -import mage.constants.SetType; - -/** - * @author North - */ -public final class Stronghold extends ExpansionSet { - - private static final Stronghold instance = new Stronghold(); - - public static Stronghold getInstance() { - return instance; - } - - private Stronghold() { - super("Stronghold", "STH", ExpansionSet.buildDate(1998, 3, 2), SetType.EXPANSION); - this.blockName = "Tempest"; - this.parentSet = Tempest.getInstance(); - this.hasBasicLands = false; - this.hasBoosters = true; - this.numBoosterLands = 0; - this.numBoosterCommon = 11; - this.numBoosterUncommon = 3; - this.numBoosterRare = 1; - this.ratioBoosterMythic = 0; - cards.add(new SetCardInfo("Acidic Sliver", 126, Rarity.UNCOMMON, mage.cards.a.AcidicSliver.class)); - cards.add(new SetCardInfo("Amok", 76, Rarity.RARE, mage.cards.a.Amok.class)); - cards.add(new SetCardInfo("Awakening", 101, Rarity.RARE, mage.cards.a.Awakening.class)); - cards.add(new SetCardInfo("Bandage", 1, Rarity.COMMON, mage.cards.b.Bandage.class)); - cards.add(new SetCardInfo("Bottomless Pit", 51, Rarity.UNCOMMON, mage.cards.b.BottomlessPit.class)); - cards.add(new SetCardInfo("Brush with Death", 52, Rarity.COMMON, mage.cards.b.BrushWithDeath.class)); - cards.add(new SetCardInfo("Bullwhip", 132, Rarity.UNCOMMON, mage.cards.b.Bullwhip.class)); - cards.add(new SetCardInfo("Burgeoning", 102, Rarity.RARE, mage.cards.b.Burgeoning.class)); - cards.add(new SetCardInfo("Calming Licid", 2, Rarity.UNCOMMON, mage.cards.c.CalmingLicid.class)); - cards.add(new SetCardInfo("Cannibalize", 53, Rarity.COMMON, mage.cards.c.Cannibalize.class)); - cards.add(new SetCardInfo("Carnassid", 103, Rarity.RARE, mage.cards.c.Carnassid.class)); - cards.add(new SetCardInfo("Change of Heart", 3, Rarity.COMMON, mage.cards.c.ChangeOfHeart.class)); - cards.add(new SetCardInfo("Cloud Spirit", 26, Rarity.COMMON, mage.cards.c.CloudSpirit.class)); - cards.add(new SetCardInfo("Constant Mists", 104, Rarity.UNCOMMON, mage.cards.c.ConstantMists.class)); - cards.add(new SetCardInfo("Contemplation", 4, Rarity.UNCOMMON, mage.cards.c.Contemplation.class)); - cards.add(new SetCardInfo("Conviction", 5, Rarity.COMMON, mage.cards.c.Conviction.class)); - cards.add(new SetCardInfo("Convulsing Licid", 77, Rarity.UNCOMMON, mage.cards.c.ConvulsingLicid.class)); - cards.add(new SetCardInfo("Corrupting Licid", 54, Rarity.UNCOMMON, mage.cards.c.CorruptingLicid.class)); - cards.add(new SetCardInfo("Craven Giant", 78, Rarity.COMMON, mage.cards.c.CravenGiant.class)); - cards.add(new SetCardInfo("Crossbow Ambush", 105, Rarity.COMMON, mage.cards.c.CrossbowAmbush.class)); - cards.add(new SetCardInfo("Crovax the Cursed", 55, Rarity.RARE, mage.cards.c.CrovaxTheCursed.class)); - cards.add(new SetCardInfo("Crystalline Sliver", 127, Rarity.UNCOMMON, mage.cards.c.CrystallineSliver.class)); - cards.add(new SetCardInfo("Dauthi Trapper", 56, Rarity.UNCOMMON, mage.cards.d.DauthiTrapper.class)); - cards.add(new SetCardInfo("Death Stroke", 57, Rarity.COMMON, mage.cards.d.DeathStroke.class)); - cards.add(new SetCardInfo("Dream Halls", 28, Rarity.RARE, mage.cards.d.DreamHalls.class)); - cards.add(new SetCardInfo("Dream Prowler", 29, Rarity.COMMON, mage.cards.d.DreamProwler.class)); - cards.add(new SetCardInfo("Duct Crawler", 79, Rarity.COMMON, mage.cards.d.DuctCrawler.class)); - cards.add(new SetCardInfo("Dungeon Shade", 58, Rarity.COMMON, mage.cards.d.DungeonShade.class)); - cards.add(new SetCardInfo("Elven Rite", 106, Rarity.UNCOMMON, mage.cards.e.ElvenRite.class)); - cards.add(new SetCardInfo("Endangered Armodon", 107, Rarity.COMMON, mage.cards.e.EndangeredArmodon.class)); - cards.add(new SetCardInfo("Ensnaring Bridge", 133, Rarity.RARE, mage.cards.e.EnsnaringBridge.class)); - cards.add(new SetCardInfo("Evacuation", 30, Rarity.RARE, mage.cards.e.Evacuation.class)); - cards.add(new SetCardInfo("Fanning the Flames", 80, Rarity.UNCOMMON, mage.cards.f.FanningTheFlames.class)); - cards.add(new SetCardInfo("Flame Wave", 81, Rarity.UNCOMMON, mage.cards.f.FlameWave.class)); - cards.add(new SetCardInfo("Fling", 82, Rarity.COMMON, mage.cards.f.Fling.class)); - cards.add(new SetCardInfo("Flowstone Blade", 83, Rarity.COMMON, mage.cards.f.FlowstoneBlade.class)); - cards.add(new SetCardInfo("Flowstone Hellion", 84, Rarity.UNCOMMON, mage.cards.f.FlowstoneHellion.class)); - cards.add(new SetCardInfo("Flowstone Mauler", 85, Rarity.RARE, mage.cards.f.FlowstoneMauler.class)); - cards.add(new SetCardInfo("Flowstone Shambler", 86, Rarity.COMMON, mage.cards.f.FlowstoneShambler.class)); - cards.add(new SetCardInfo("Foul Imp", 59, Rarity.COMMON, mage.cards.f.FoulImp.class)); - cards.add(new SetCardInfo("Furnace Spirit", 87, Rarity.COMMON, mage.cards.f.FurnaceSpirit.class)); - cards.add(new SetCardInfo("Gliding Licid", 31, Rarity.UNCOMMON, mage.cards.g.GlidingLicid.class)); - cards.add(new SetCardInfo("Grave Pact", 60, Rarity.RARE, mage.cards.g.GravePact.class)); - cards.add(new SetCardInfo("Hammerhead Shark", 32, Rarity.COMMON, mage.cards.h.HammerheadShark.class)); - cards.add(new SetCardInfo("Heartstone", 134, Rarity.UNCOMMON, mage.cards.h.Heartstone.class)); - cards.add(new SetCardInfo("Heat of Battle", 88, Rarity.UNCOMMON, mage.cards.h.HeatOfBattle.class)); - cards.add(new SetCardInfo("Hermit Druid", 108, Rarity.RARE, mage.cards.h.HermitDruid.class)); - cards.add(new SetCardInfo("Hesitation", 33, Rarity.UNCOMMON, mage.cards.h.Hesitation.class)); - cards.add(new SetCardInfo("Hibernation Sliver", 128, Rarity.UNCOMMON, mage.cards.h.HibernationSliver.class)); - cards.add(new SetCardInfo("Honor Guard", 7, Rarity.COMMON, mage.cards.h.HonorGuard.class)); - cards.add(new SetCardInfo("Horn of Greed", 135, Rarity.RARE, mage.cards.h.HornOfGreed.class)); - cards.add(new SetCardInfo("Hornet Cannon", 136, Rarity.UNCOMMON, mage.cards.h.HornetCannon.class)); - cards.add(new SetCardInfo("Intruder Alarm", 34, Rarity.RARE, mage.cards.i.IntruderAlarm.class)); - cards.add(new SetCardInfo("Invasion Plans", 89, Rarity.RARE, mage.cards.i.InvasionPlans.class)); - cards.add(new SetCardInfo("Jinxed Ring", 137, Rarity.RARE, mage.cards.j.JinxedRing.class)); - cards.add(new SetCardInfo("Lab Rats", 61, Rarity.COMMON, mage.cards.l.LabRats.class)); - cards.add(new SetCardInfo("Lancers en-Kor", 8, Rarity.UNCOMMON, mage.cards.l.LancersEnKor.class)); - cards.add(new SetCardInfo("Leap", 35, Rarity.COMMON, mage.cards.l.Leap.class)); - cards.add(new SetCardInfo("Lowland Basilisk", 109, Rarity.COMMON, mage.cards.l.LowlandBasilisk.class)); - cards.add(new SetCardInfo("Mana Leak", 36, Rarity.COMMON, mage.cards.m.ManaLeak.class)); - cards.add(new SetCardInfo("Mask of the Mimic", 37, Rarity.UNCOMMON, mage.cards.m.MaskOfTheMimic.class)); - cards.add(new SetCardInfo("Megrim", 62, Rarity.UNCOMMON, mage.cards.m.Megrim.class)); - cards.add(new SetCardInfo("Mind Games", 38, Rarity.COMMON, mage.cards.m.MindGames.class)); - cards.add(new SetCardInfo("Mind Peel", 63, Rarity.UNCOMMON, mage.cards.m.MindPeel.class)); - cards.add(new SetCardInfo("Mindwarper", 64, Rarity.RARE, mage.cards.m.Mindwarper.class)); - cards.add(new SetCardInfo("Mob Justice", 90, Rarity.COMMON, mage.cards.m.MobJustice.class)); - cards.add(new SetCardInfo("Mogg Flunkies", 92, Rarity.COMMON, mage.cards.m.MoggFlunkies.class)); - cards.add(new SetCardInfo("Mogg Infestation", 93, Rarity.RARE, mage.cards.m.MoggInfestation.class)); - cards.add(new SetCardInfo("Mogg Maniac", 94, Rarity.UNCOMMON, mage.cards.m.MoggManiac.class)); - cards.add(new SetCardInfo("Morgue Thrull", 65, Rarity.COMMON, mage.cards.m.MorgueThrull.class)); - cards.add(new SetCardInfo("Mortuary", 66, Rarity.RARE, mage.cards.m.Mortuary.class)); - cards.add(new SetCardInfo("Mox Diamond", 138, Rarity.RARE, mage.cards.m.MoxDiamond.class)); - cards.add(new SetCardInfo("Mulch", 110, Rarity.COMMON, mage.cards.m.Mulch.class)); - cards.add(new SetCardInfo("Nomads en-Kor", 9, Rarity.COMMON, mage.cards.n.NomadsEnKor.class)); - cards.add(new SetCardInfo("Overgrowth", 111, Rarity.COMMON, mage.cards.o.Overgrowth.class)); - cards.add(new SetCardInfo("Portcullis", 139, Rarity.RARE, mage.cards.p.Portcullis.class)); - cards.add(new SetCardInfo("Primal Rage", 112, Rarity.UNCOMMON, mage.cards.p.PrimalRage.class)); - cards.add(new SetCardInfo("Provoke", 113, Rarity.COMMON, mage.cards.p.Provoke.class)); - cards.add(new SetCardInfo("Pursuit of Knowledge", 10, Rarity.RARE, mage.cards.p.PursuitOfKnowledge.class)); - cards.add(new SetCardInfo("Rabid Rats", 67, Rarity.COMMON, mage.cards.r.RabidRats.class)); - cards.add(new SetCardInfo("Reins of Power", 41, Rarity.RARE, mage.cards.r.ReinsOfPower.class)); - cards.add(new SetCardInfo("Revenant", 68, Rarity.RARE, mage.cards.r.Revenant.class)); - cards.add(new SetCardInfo("Rolling Stones", 11, Rarity.RARE, mage.cards.r.RollingStones.class)); - cards.add(new SetCardInfo("Ruination", 95, Rarity.RARE, mage.cards.r.Ruination.class)); - cards.add(new SetCardInfo("Sacred Ground", 12, Rarity.RARE, mage.cards.s.SacredGround.class)); - cards.add(new SetCardInfo("Scapegoat", 14, Rarity.UNCOMMON, mage.cards.s.Scapegoat.class)); - cards.add(new SetCardInfo("Seething Anger", 96, Rarity.COMMON, mage.cards.s.SeethingAnger.class)); - cards.add(new SetCardInfo("Serpent Warrior", 69, Rarity.COMMON, mage.cards.s.SerpentWarrior.class)); - cards.add(new SetCardInfo("Shaman en-Kor", 15, Rarity.RARE, mage.cards.s.ShamanEnKor.class)); - cards.add(new SetCardInfo("Shard Phoenix", 97, Rarity.RARE, mage.cards.s.ShardPhoenix.class)); - cards.add(new SetCardInfo("Shifting Wall", 140, Rarity.UNCOMMON, mage.cards.s.ShiftingWall.class)); - cards.add(new SetCardInfo("Shock", 98, Rarity.COMMON, mage.cards.s.Shock.class)); - cards.add(new SetCardInfo("Sift", 42, Rarity.COMMON, mage.cards.s.Sift.class)); - cards.add(new SetCardInfo("Silver Wyvern", 43, Rarity.RARE, mage.cards.s.SilverWyvern.class)); - cards.add(new SetCardInfo("Skyshroud Archer", 114, Rarity.COMMON, mage.cards.s.SkyshroudArcher.class)); - cards.add(new SetCardInfo("Skyshroud Falcon", 16, Rarity.COMMON, mage.cards.s.SkyshroudFalcon.class)); - cards.add(new SetCardInfo("Skyshroud Troopers", 115, Rarity.COMMON, mage.cards.s.SkyshroudTroopers.class)); - cards.add(new SetCardInfo("Sliver Queen", 129, Rarity.RARE, mage.cards.s.SliverQueen.class)); - cards.add(new SetCardInfo("Smite", 17, Rarity.COMMON, mage.cards.s.Smite.class)); - cards.add(new SetCardInfo("Soltari Champion", 18, Rarity.RARE, mage.cards.s.SoltariChampion.class)); - cards.add(new SetCardInfo("Spike Breeder", 116, Rarity.RARE, mage.cards.s.SpikeBreeder.class)); - cards.add(new SetCardInfo("Spike Colony", 117, Rarity.COMMON, mage.cards.s.SpikeColony.class)); - cards.add(new SetCardInfo("Spike Feeder", 118, Rarity.UNCOMMON, mage.cards.s.SpikeFeeder.class)); - cards.add(new SetCardInfo("Spike Soldier", 119, Rarity.UNCOMMON, mage.cards.s.SpikeSoldier.class)); - cards.add(new SetCardInfo("Spike Worker", 120, Rarity.COMMON, mage.cards.s.SpikeWorker.class)); - cards.add(new SetCardInfo("Spindrift Drake", 44, Rarity.COMMON, mage.cards.s.SpindriftDrake.class)); - cards.add(new SetCardInfo("Spined Sliver", 130, Rarity.UNCOMMON, mage.cards.s.SpinedSliver.class)); - cards.add(new SetCardInfo("Spined Wurm", 121, Rarity.COMMON, mage.cards.s.SpinedWurm.class)); - cards.add(new SetCardInfo("Spirit en-Kor", 19, Rarity.COMMON, mage.cards.s.SpiritEnKor.class)); - cards.add(new SetCardInfo("Spitting Hydra", 99, Rarity.RARE, mage.cards.s.SpittingHydra.class)); - cards.add(new SetCardInfo("Stronghold Assassin", 71, Rarity.RARE, mage.cards.s.StrongholdAssassin.class)); - cards.add(new SetCardInfo("Stronghold Taskmaster", 72, Rarity.UNCOMMON, mage.cards.s.StrongholdTaskmaster.class)); - cards.add(new SetCardInfo("Sword of the Chosen", 141, Rarity.RARE, mage.cards.s.SwordOfTheChosen.class)); - cards.add(new SetCardInfo("Temper", 20, Rarity.UNCOMMON, mage.cards.t.Temper.class)); - cards.add(new SetCardInfo("Tempting Licid", 122, Rarity.UNCOMMON, mage.cards.t.TemptingLicid.class)); - cards.add(new SetCardInfo("Thalakos Deceiver", 45, Rarity.RARE, mage.cards.t.ThalakosDeceiver.class)); - cards.add(new SetCardInfo("Tidal Surge", 46, Rarity.COMMON, mage.cards.t.TidalSurge.class)); - cards.add(new SetCardInfo("Tidal Warrior", 47, Rarity.COMMON, mage.cards.t.TidalWarrior.class)); - cards.add(new SetCardInfo("Torment", 73, Rarity.COMMON, mage.cards.t.Torment.class)); - cards.add(new SetCardInfo("Tortured Existence", 74, Rarity.COMMON, mage.cards.t.TorturedExistence.class)); - cards.add(new SetCardInfo("Venerable Monk", 21, Rarity.COMMON, mage.cards.v.VenerableMonk.class)); - cards.add(new SetCardInfo("Verdant Touch", 123, Rarity.RARE, mage.cards.v.VerdantTouch.class)); - cards.add(new SetCardInfo("Victual Sliver", 131, Rarity.UNCOMMON, mage.cards.v.VictualSliver.class)); - cards.add(new SetCardInfo("Volrath's Gardens", 124, Rarity.RARE, mage.cards.v.VolrathsGardens.class)); - cards.add(new SetCardInfo("Volrath's Laboratory", 142, Rarity.RARE, mage.cards.v.VolrathsLaboratory.class)); - cards.add(new SetCardInfo("Volrath's Shapeshifter", 48, Rarity.RARE, mage.cards.v.VolrathsShapeshifter.class)); - cards.add(new SetCardInfo("Volrath's Stronghold", 143, Rarity.RARE, mage.cards.v.VolrathsStronghold.class)); - cards.add(new SetCardInfo("Wall of Blossoms", 125, Rarity.UNCOMMON, mage.cards.w.WallOfBlossoms.class)); - cards.add(new SetCardInfo("Wall of Essence", 22, Rarity.UNCOMMON, mage.cards.w.WallOfEssence.class)); - cards.add(new SetCardInfo("Wall of Razors", 100, Rarity.UNCOMMON, mage.cards.w.WallOfRazors.class)); - cards.add(new SetCardInfo("Wall of Souls", 75, Rarity.UNCOMMON, mage.cards.w.WallOfSouls.class)); - cards.add(new SetCardInfo("Wall of Tears", 50, Rarity.UNCOMMON, mage.cards.w.WallOfTears.class)); - cards.add(new SetCardInfo("Warrior Angel", 24, Rarity.RARE, mage.cards.w.WarriorAngel.class)); - cards.add(new SetCardInfo("Warrior en-Kor", 23, Rarity.UNCOMMON, mage.cards.w.WarriorEnKor.class)); - cards.add(new SetCardInfo("Youthful Knight", 25, Rarity.COMMON, mage.cards.y.YouthfulKnight.class)); - } -} + +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author North + */ +public final class Stronghold extends ExpansionSet { + + private static final Stronghold instance = new Stronghold(); + + public static Stronghold getInstance() { + return instance; + } + + private Stronghold() { + super("Stronghold", "STH", ExpansionSet.buildDate(1998, 3, 2), SetType.EXPANSION); + this.blockName = "Tempest"; + this.parentSet = Tempest.getInstance(); + this.hasBasicLands = false; + this.hasBoosters = true; + this.numBoosterLands = 0; + this.numBoosterCommon = 11; + this.numBoosterUncommon = 3; + this.numBoosterRare = 1; + this.ratioBoosterMythic = 0; + cards.add(new SetCardInfo("Acidic Sliver", 126, Rarity.UNCOMMON, mage.cards.a.AcidicSliver.class)); + cards.add(new SetCardInfo("Amok", 76, Rarity.RARE, mage.cards.a.Amok.class)); + cards.add(new SetCardInfo("Awakening", 101, Rarity.RARE, mage.cards.a.Awakening.class)); + cards.add(new SetCardInfo("Bandage", 1, Rarity.COMMON, mage.cards.b.Bandage.class)); + cards.add(new SetCardInfo("Bottomless Pit", 51, Rarity.UNCOMMON, mage.cards.b.BottomlessPit.class)); + cards.add(new SetCardInfo("Brush with Death", 52, Rarity.COMMON, mage.cards.b.BrushWithDeath.class)); + cards.add(new SetCardInfo("Bullwhip", 132, Rarity.UNCOMMON, mage.cards.b.Bullwhip.class)); + cards.add(new SetCardInfo("Burgeoning", 102, Rarity.RARE, mage.cards.b.Burgeoning.class)); + cards.add(new SetCardInfo("Calming Licid", 2, Rarity.UNCOMMON, mage.cards.c.CalmingLicid.class)); + cards.add(new SetCardInfo("Cannibalize", 53, Rarity.COMMON, mage.cards.c.Cannibalize.class)); + cards.add(new SetCardInfo("Carnassid", 103, Rarity.RARE, mage.cards.c.Carnassid.class)); + cards.add(new SetCardInfo("Change of Heart", 3, Rarity.COMMON, mage.cards.c.ChangeOfHeart.class)); + cards.add(new SetCardInfo("Cloud Spirit", 26, Rarity.COMMON, mage.cards.c.CloudSpirit.class)); + cards.add(new SetCardInfo("Constant Mists", 104, Rarity.UNCOMMON, mage.cards.c.ConstantMists.class)); + cards.add(new SetCardInfo("Contemplation", 4, Rarity.UNCOMMON, mage.cards.c.Contemplation.class)); + cards.add(new SetCardInfo("Conviction", 5, Rarity.COMMON, mage.cards.c.Conviction.class)); + cards.add(new SetCardInfo("Convulsing Licid", 77, Rarity.UNCOMMON, mage.cards.c.ConvulsingLicid.class)); + cards.add(new SetCardInfo("Corrupting Licid", 54, Rarity.UNCOMMON, mage.cards.c.CorruptingLicid.class)); + cards.add(new SetCardInfo("Craven Giant", 78, Rarity.COMMON, mage.cards.c.CravenGiant.class)); + cards.add(new SetCardInfo("Crossbow Ambush", 105, Rarity.COMMON, mage.cards.c.CrossbowAmbush.class)); + cards.add(new SetCardInfo("Crovax the Cursed", 55, Rarity.RARE, mage.cards.c.CrovaxTheCursed.class)); + cards.add(new SetCardInfo("Crystalline Sliver", 127, Rarity.UNCOMMON, mage.cards.c.CrystallineSliver.class)); + cards.add(new SetCardInfo("Dauthi Trapper", 56, Rarity.UNCOMMON, mage.cards.d.DauthiTrapper.class)); + cards.add(new SetCardInfo("Death Stroke", 57, Rarity.COMMON, mage.cards.d.DeathStroke.class)); + cards.add(new SetCardInfo("Dream Halls", 28, Rarity.RARE, mage.cards.d.DreamHalls.class)); + cards.add(new SetCardInfo("Dream Prowler", 29, Rarity.COMMON, mage.cards.d.DreamProwler.class)); + cards.add(new SetCardInfo("Duct Crawler", 79, Rarity.COMMON, mage.cards.d.DuctCrawler.class)); + cards.add(new SetCardInfo("Dungeon Shade", 58, Rarity.COMMON, mage.cards.d.DungeonShade.class)); + cards.add(new SetCardInfo("Elven Rite", 106, Rarity.UNCOMMON, mage.cards.e.ElvenRite.class)); + cards.add(new SetCardInfo("Endangered Armodon", 107, Rarity.COMMON, mage.cards.e.EndangeredArmodon.class)); + cards.add(new SetCardInfo("Ensnaring Bridge", 133, Rarity.RARE, mage.cards.e.EnsnaringBridge.class)); + cards.add(new SetCardInfo("Evacuation", 30, Rarity.RARE, mage.cards.e.Evacuation.class)); + cards.add(new SetCardInfo("Fanning the Flames", 80, Rarity.UNCOMMON, mage.cards.f.FanningTheFlames.class)); + cards.add(new SetCardInfo("Flame Wave", 81, Rarity.UNCOMMON, mage.cards.f.FlameWave.class)); + cards.add(new SetCardInfo("Fling", 82, Rarity.COMMON, mage.cards.f.Fling.class)); + cards.add(new SetCardInfo("Flowstone Blade", 83, Rarity.COMMON, mage.cards.f.FlowstoneBlade.class)); + cards.add(new SetCardInfo("Flowstone Hellion", 84, Rarity.UNCOMMON, mage.cards.f.FlowstoneHellion.class)); + cards.add(new SetCardInfo("Flowstone Mauler", 85, Rarity.RARE, mage.cards.f.FlowstoneMauler.class)); + cards.add(new SetCardInfo("Flowstone Shambler", 86, Rarity.COMMON, mage.cards.f.FlowstoneShambler.class)); + cards.add(new SetCardInfo("Foul Imp", 59, Rarity.COMMON, mage.cards.f.FoulImp.class)); + cards.add(new SetCardInfo("Furnace Spirit", 87, Rarity.COMMON, mage.cards.f.FurnaceSpirit.class)); + cards.add(new SetCardInfo("Gliding Licid", 31, Rarity.UNCOMMON, mage.cards.g.GlidingLicid.class)); + cards.add(new SetCardInfo("Grave Pact", 60, Rarity.RARE, mage.cards.g.GravePact.class)); + cards.add(new SetCardInfo("Hammerhead Shark", 32, Rarity.COMMON, mage.cards.h.HammerheadShark.class)); + cards.add(new SetCardInfo("Heartstone", 134, Rarity.UNCOMMON, mage.cards.h.Heartstone.class)); + cards.add(new SetCardInfo("Heat of Battle", 88, Rarity.UNCOMMON, mage.cards.h.HeatOfBattle.class)); + cards.add(new SetCardInfo("Hermit Druid", 108, Rarity.RARE, mage.cards.h.HermitDruid.class)); + cards.add(new SetCardInfo("Hesitation", 33, Rarity.UNCOMMON, mage.cards.h.Hesitation.class)); + cards.add(new SetCardInfo("Hibernation Sliver", 128, Rarity.UNCOMMON, mage.cards.h.HibernationSliver.class)); + cards.add(new SetCardInfo("Honor Guard", 7, Rarity.COMMON, mage.cards.h.HonorGuard.class)); + cards.add(new SetCardInfo("Horn of Greed", 135, Rarity.RARE, mage.cards.h.HornOfGreed.class)); + cards.add(new SetCardInfo("Hornet Cannon", 136, Rarity.UNCOMMON, mage.cards.h.HornetCannon.class)); + cards.add(new SetCardInfo("Intruder Alarm", 34, Rarity.RARE, mage.cards.i.IntruderAlarm.class)); + cards.add(new SetCardInfo("Invasion Plans", 89, Rarity.RARE, mage.cards.i.InvasionPlans.class)); + cards.add(new SetCardInfo("Jinxed Ring", 137, Rarity.RARE, mage.cards.j.JinxedRing.class)); + cards.add(new SetCardInfo("Lab Rats", 61, Rarity.COMMON, mage.cards.l.LabRats.class)); + cards.add(new SetCardInfo("Lancers en-Kor", 8, Rarity.UNCOMMON, mage.cards.l.LancersEnKor.class)); + cards.add(new SetCardInfo("Leap", 35, Rarity.COMMON, mage.cards.l.Leap.class)); + cards.add(new SetCardInfo("Lowland Basilisk", 109, Rarity.COMMON, mage.cards.l.LowlandBasilisk.class)); + cards.add(new SetCardInfo("Mana Leak", 36, Rarity.COMMON, mage.cards.m.ManaLeak.class)); + cards.add(new SetCardInfo("Mask of the Mimic", 37, Rarity.UNCOMMON, mage.cards.m.MaskOfTheMimic.class)); + cards.add(new SetCardInfo("Megrim", 62, Rarity.UNCOMMON, mage.cards.m.Megrim.class)); + cards.add(new SetCardInfo("Mind Games", 38, Rarity.COMMON, mage.cards.m.MindGames.class)); + cards.add(new SetCardInfo("Mind Peel", 63, Rarity.UNCOMMON, mage.cards.m.MindPeel.class)); + cards.add(new SetCardInfo("Mindwarper", 64, Rarity.RARE, mage.cards.m.Mindwarper.class)); + cards.add(new SetCardInfo("Mob Justice", 90, Rarity.COMMON, mage.cards.m.MobJustice.class)); + cards.add(new SetCardInfo("Mogg Bombers", 91, Rarity.COMMON, mage.cards.m.MoggBombers.class)); + cards.add(new SetCardInfo("Mogg Flunkies", 92, Rarity.COMMON, mage.cards.m.MoggFlunkies.class)); + cards.add(new SetCardInfo("Mogg Infestation", 93, Rarity.RARE, mage.cards.m.MoggInfestation.class)); + cards.add(new SetCardInfo("Mogg Maniac", 94, Rarity.UNCOMMON, mage.cards.m.MoggManiac.class)); + cards.add(new SetCardInfo("Morgue Thrull", 65, Rarity.COMMON, mage.cards.m.MorgueThrull.class)); + cards.add(new SetCardInfo("Mortuary", 66, Rarity.RARE, mage.cards.m.Mortuary.class)); + cards.add(new SetCardInfo("Mox Diamond", 138, Rarity.RARE, mage.cards.m.MoxDiamond.class)); + cards.add(new SetCardInfo("Mulch", 110, Rarity.COMMON, mage.cards.m.Mulch.class)); + cards.add(new SetCardInfo("Nomads en-Kor", 9, Rarity.COMMON, mage.cards.n.NomadsEnKor.class)); + cards.add(new SetCardInfo("Overgrowth", 111, Rarity.COMMON, mage.cards.o.Overgrowth.class)); + cards.add(new SetCardInfo("Portcullis", 139, Rarity.RARE, mage.cards.p.Portcullis.class)); + cards.add(new SetCardInfo("Primal Rage", 112, Rarity.UNCOMMON, mage.cards.p.PrimalRage.class)); + cards.add(new SetCardInfo("Provoke", 113, Rarity.COMMON, mage.cards.p.Provoke.class)); + cards.add(new SetCardInfo("Pursuit of Knowledge", 10, Rarity.RARE, mage.cards.p.PursuitOfKnowledge.class)); + cards.add(new SetCardInfo("Rabid Rats", 67, Rarity.COMMON, mage.cards.r.RabidRats.class)); + cards.add(new SetCardInfo("Reins of Power", 41, Rarity.RARE, mage.cards.r.ReinsOfPower.class)); + cards.add(new SetCardInfo("Revenant", 68, Rarity.RARE, mage.cards.r.Revenant.class)); + cards.add(new SetCardInfo("Rolling Stones", 11, Rarity.RARE, mage.cards.r.RollingStones.class)); + cards.add(new SetCardInfo("Ruination", 95, Rarity.RARE, mage.cards.r.Ruination.class)); + cards.add(new SetCardInfo("Sacred Ground", 12, Rarity.RARE, mage.cards.s.SacredGround.class)); + cards.add(new SetCardInfo("Scapegoat", 14, Rarity.UNCOMMON, mage.cards.s.Scapegoat.class)); + cards.add(new SetCardInfo("Seething Anger", 96, Rarity.COMMON, mage.cards.s.SeethingAnger.class)); + cards.add(new SetCardInfo("Serpent Warrior", 69, Rarity.COMMON, mage.cards.s.SerpentWarrior.class)); + cards.add(new SetCardInfo("Shaman en-Kor", 15, Rarity.RARE, mage.cards.s.ShamanEnKor.class)); + cards.add(new SetCardInfo("Shard Phoenix", 97, Rarity.RARE, mage.cards.s.ShardPhoenix.class)); + cards.add(new SetCardInfo("Shifting Wall", 140, Rarity.UNCOMMON, mage.cards.s.ShiftingWall.class)); + cards.add(new SetCardInfo("Shock", 98, Rarity.COMMON, mage.cards.s.Shock.class)); + cards.add(new SetCardInfo("Sift", 42, Rarity.COMMON, mage.cards.s.Sift.class)); + cards.add(new SetCardInfo("Silver Wyvern", 43, Rarity.RARE, mage.cards.s.SilverWyvern.class)); + cards.add(new SetCardInfo("Skyshroud Archer", 114, Rarity.COMMON, mage.cards.s.SkyshroudArcher.class)); + cards.add(new SetCardInfo("Skyshroud Falcon", 16, Rarity.COMMON, mage.cards.s.SkyshroudFalcon.class)); + cards.add(new SetCardInfo("Skyshroud Troopers", 115, Rarity.COMMON, mage.cards.s.SkyshroudTroopers.class)); + cards.add(new SetCardInfo("Sliver Queen", 129, Rarity.RARE, mage.cards.s.SliverQueen.class)); + cards.add(new SetCardInfo("Smite", 17, Rarity.COMMON, mage.cards.s.Smite.class)); + cards.add(new SetCardInfo("Soltari Champion", 18, Rarity.RARE, mage.cards.s.SoltariChampion.class)); + cards.add(new SetCardInfo("Spike Breeder", 116, Rarity.RARE, mage.cards.s.SpikeBreeder.class)); + cards.add(new SetCardInfo("Spike Colony", 117, Rarity.COMMON, mage.cards.s.SpikeColony.class)); + cards.add(new SetCardInfo("Spike Feeder", 118, Rarity.UNCOMMON, mage.cards.s.SpikeFeeder.class)); + cards.add(new SetCardInfo("Spike Soldier", 119, Rarity.UNCOMMON, mage.cards.s.SpikeSoldier.class)); + cards.add(new SetCardInfo("Spike Worker", 120, Rarity.COMMON, mage.cards.s.SpikeWorker.class)); + cards.add(new SetCardInfo("Spindrift Drake", 44, Rarity.COMMON, mage.cards.s.SpindriftDrake.class)); + cards.add(new SetCardInfo("Spined Sliver", 130, Rarity.UNCOMMON, mage.cards.s.SpinedSliver.class)); + cards.add(new SetCardInfo("Spined Wurm", 121, Rarity.COMMON, mage.cards.s.SpinedWurm.class)); + cards.add(new SetCardInfo("Spirit en-Kor", 19, Rarity.COMMON, mage.cards.s.SpiritEnKor.class)); + cards.add(new SetCardInfo("Spitting Hydra", 99, Rarity.RARE, mage.cards.s.SpittingHydra.class)); + cards.add(new SetCardInfo("Stronghold Assassin", 71, Rarity.RARE, mage.cards.s.StrongholdAssassin.class)); + cards.add(new SetCardInfo("Stronghold Taskmaster", 72, Rarity.UNCOMMON, mage.cards.s.StrongholdTaskmaster.class)); + cards.add(new SetCardInfo("Sword of the Chosen", 141, Rarity.RARE, mage.cards.s.SwordOfTheChosen.class)); + cards.add(new SetCardInfo("Temper", 20, Rarity.UNCOMMON, mage.cards.t.Temper.class)); + cards.add(new SetCardInfo("Tempting Licid", 122, Rarity.UNCOMMON, mage.cards.t.TemptingLicid.class)); + cards.add(new SetCardInfo("Thalakos Deceiver", 45, Rarity.RARE, mage.cards.t.ThalakosDeceiver.class)); + cards.add(new SetCardInfo("Tidal Surge", 46, Rarity.COMMON, mage.cards.t.TidalSurge.class)); + cards.add(new SetCardInfo("Tidal Warrior", 47, Rarity.COMMON, mage.cards.t.TidalWarrior.class)); + cards.add(new SetCardInfo("Torment", 73, Rarity.COMMON, mage.cards.t.Torment.class)); + cards.add(new SetCardInfo("Tortured Existence", 74, Rarity.COMMON, mage.cards.t.TorturedExistence.class)); + cards.add(new SetCardInfo("Venerable Monk", 21, Rarity.COMMON, mage.cards.v.VenerableMonk.class)); + cards.add(new SetCardInfo("Verdant Touch", 123, Rarity.RARE, mage.cards.v.VerdantTouch.class)); + cards.add(new SetCardInfo("Victual Sliver", 131, Rarity.UNCOMMON, mage.cards.v.VictualSliver.class)); + cards.add(new SetCardInfo("Volrath's Gardens", 124, Rarity.RARE, mage.cards.v.VolrathsGardens.class)); + cards.add(new SetCardInfo("Volrath's Laboratory", 142, Rarity.RARE, mage.cards.v.VolrathsLaboratory.class)); + cards.add(new SetCardInfo("Volrath's Shapeshifter", 48, Rarity.RARE, mage.cards.v.VolrathsShapeshifter.class)); + cards.add(new SetCardInfo("Volrath's Stronghold", 143, Rarity.RARE, mage.cards.v.VolrathsStronghold.class)); + cards.add(new SetCardInfo("Walking Dream", 49, Rarity.UNCOMMON, mage.cards.w.WalkingDream.class)); + cards.add(new SetCardInfo("Wall of Blossoms", 125, Rarity.UNCOMMON, mage.cards.w.WallOfBlossoms.class)); + cards.add(new SetCardInfo("Wall of Essence", 22, Rarity.UNCOMMON, mage.cards.w.WallOfEssence.class)); + cards.add(new SetCardInfo("Wall of Razors", 100, Rarity.UNCOMMON, mage.cards.w.WallOfRazors.class)); + cards.add(new SetCardInfo("Wall of Souls", 75, Rarity.UNCOMMON, mage.cards.w.WallOfSouls.class)); + cards.add(new SetCardInfo("Wall of Tears", 50, Rarity.UNCOMMON, mage.cards.w.WallOfTears.class)); + cards.add(new SetCardInfo("Warrior Angel", 24, Rarity.RARE, mage.cards.w.WarriorAngel.class)); + cards.add(new SetCardInfo("Warrior en-Kor", 23, Rarity.UNCOMMON, mage.cards.w.WarriorEnKor.class)); + cards.add(new SetCardInfo("Youthful Knight", 25, Rarity.COMMON, mage.cards.y.YouthfulKnight.class)); + } +} From 09a5ece9f301eb10110a06e2812b26ef6025c7be Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 27 Nov 2018 16:09:43 +0100 Subject: [PATCH 60/61] Fixed color check and added missing attribute to verify. --- Mage.Verify/src/main/java/mage/verify/JsonCard.java | 2 ++ .../src/test/java/mage/verify/VerifyCardDataTest.java | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Mage.Verify/src/main/java/mage/verify/JsonCard.java b/Mage.Verify/src/main/java/mage/verify/JsonCard.java index c7a0c49a4b5..ef857d8d751 100644 --- a/Mage.Verify/src/main/java/mage/verify/JsonCard.java +++ b/Mage.Verify/src/main/java/mage/verify/JsonCard.java @@ -3,6 +3,7 @@ package mage.verify; import java.util.List; class JsonCard { + public String uuid; public String convertedManaCost; public List foreignData; @@ -56,4 +57,5 @@ class JsonCard { public boolean timeshifted; public String borderColor; public boolean isOversized; + public String faceConvertedManaCost; } diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index d170b83cd57..0ebbe0f3e40 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -575,11 +575,11 @@ public class VerifyCardDataTest { ObjectColor color = card.getColor(null); if (expected.size() != color.getColorCount() - || (color.isBlack() && !expected.contains("Black")) - || (color.isBlue() && !expected.contains("Blue")) - || (color.isGreen() && !expected.contains("Green")) - || (color.isRed() && !expected.contains("Red")) - || (color.isWhite() && !expected.contains("White"))) { + || (color.isBlack() && !expected.contains("B")) + || (color.isBlue() && !expected.contains("U")) + || (color.isGreen() && !expected.contains("G")) + || (color.isRed() && !expected.contains("R")) + || (color.isWhite() && !expected.contains("W"))) { fail(card, "colors", color + " != " + expected); } } From 258bb2b62382ad2ef3c3a968f543519ba25cba26 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 28 Nov 2018 15:43:28 -0600 Subject: [PATCH 61/61] - Added Skeleton Scavengers and Contempt. --- Mage.Sets/src/mage/cards/c/Contempt.java | 93 +++++++++++++ .../src/mage/cards/s/SkeletonScavengers.java | 129 ++++++++++++++++++ Mage.Sets/src/mage/sets/Stronghold.java | 2 + 3 files changed, 224 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/c/Contempt.java create mode 100644 Mage.Sets/src/mage/cards/s/SkeletonScavengers.java diff --git a/Mage.Sets/src/mage/cards/c/Contempt.java b/Mage.Sets/src/mage/cards/c/Contempt.java new file mode 100644 index 00000000000..1aa4800d05e --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/Contempt.java @@ -0,0 +1,93 @@ +package mage.cards.c; + +import java.util.UUID; +import mage.constants.SubType; +import mage.target.common.TargetCreaturePermanent; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAttachedTriggeredAbility; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.ReturnToHandSourceEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.constants.Outcome; +import mage.target.TargetPermanent; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author jeffwadsworth + */ +public final class Contempt extends CardImpl { + + public Contempt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Whenever enchanted creature attacks, return it and Contempt to their owners' hands at end of combat. + this.addAbility(new AttacksAttachedTriggeredAbility(new ContemptEffect())); + + } + + public Contempt(final Contempt card) { + super(card); + } + + @Override + public Contempt copy() { + return new Contempt(this); + } +} + +class ContemptEffect extends OneShotEffect { + + ContemptEffect() { + super(Outcome.Detriment); + this.staticText = "return it and {this} to their owners' hands at end of combat."; + } + + ContemptEffect(final ContemptEffect effect) { + super(effect); + } + + @Override + public ContemptEffect copy() { + return new ContemptEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent contempt = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (contempt != null) { + Permanent attachedToPermanent = game.getPermanent(contempt.getAttachedTo()); + if (attachedToPermanent != null) { + Effect effect = new ReturnToHandTargetEffect(); + effect.setTargetPointer(new FixedTarget( + attachedToPermanent.getId())).setText("return " + + attachedToPermanent.getName() + " to owner's hand."); + AtTheEndOfCombatDelayedTriggeredAbility ability = new AtTheEndOfCombatDelayedTriggeredAbility(effect); + game.addDelayedTriggeredAbility(ability, source); + } + Effect effect = new ReturnToHandSourceEffect(); + AtTheEndOfCombatDelayedTriggeredAbility ability = new AtTheEndOfCombatDelayedTriggeredAbility(effect); + game.addDelayedTriggeredAbility(ability, source); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SkeletonScavengers.java b/Mage.Sets/src/mage/cards/s/SkeletonScavengers.java new file mode 100644 index 00000000000..c15d68bdd04 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkeletonScavengers.java @@ -0,0 +1,129 @@ +package mage.cards.s; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.constants.SubType; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author jeffwadsworth + */ +public final class SkeletonScavengers extends CardImpl { + + public SkeletonScavengers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.SKELETON); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Skeleton Scavengers enters the battlefield with a +1/+1 counter on it. + this.addAbility(new AsEntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()))); + + // Pay {1} for each +1/+1 counter on Skeleton Scavengers: Regenerate Skeleton Scavengers. When it regenerates this way, put a +1/+1 counter on it. + this.addAbility(new SimpleActivatedAbility(new SkeletonScavengersEffect(), new DynamicValueGenericManaCost(new CountersSourceCount(CounterType.P1P1)))); + + } + + public SkeletonScavengers(final SkeletonScavengers card) { + super(card); + } + + @Override + public SkeletonScavengers copy() { + return new SkeletonScavengers(this); + } +} + +class DynamicValueGenericManaCost extends CostImpl { + + DynamicValue amount; + + public DynamicValueGenericManaCost(DynamicValue amount) { + this.amount = amount; + setText(); + } + + public DynamicValueGenericManaCost(DynamicValueGenericManaCost cost) { + super(cost); + this.amount = cost.amount; + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + Player controller = game.getPlayer(controllerId); + if (controller == null) { + return false; + } + int convertedCost = amount.calculate(game, ability, null); + Cost cost = new GenericManaCost(convertedCost); + return cost.canPay(ability, sourceId, controllerId, game); + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + Player controller = game.getPlayer(controllerId); + int convertedCost = amount.calculate(game, ability, null); + Cost cost = new GenericManaCost(convertedCost); + if (controller != null) { + paid = cost.pay(ability, game, sourceId, controllerId, noMana); + } + return paid; + } + + @Override + public DynamicValueGenericManaCost copy() { + return new DynamicValueGenericManaCost(this); + } + + private void setText() { + text = ("Pay {1} for each +1/+1 counter on {this}"); + } +} + +class SkeletonScavengersEffect extends OneShotEffect { + + SkeletonScavengersEffect() { + super(Outcome.Benefit); + this.staticText = "Regenerate {this}. When it regenerates this way, put a +1/+1 counter on it"; + } + + SkeletonScavengersEffect(final SkeletonScavengersEffect effect) { + super(effect); + } + + @Override + public SkeletonScavengersEffect copy() { + return new SkeletonScavengersEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent skeletonScavengers = game.getPermanent(source.getSourceId()); + if (skeletonScavengers != null) { + if (new RegenerateSourceEffect().apply(game, source)) { + return new AddCountersSourceEffect(CounterType.P1P1.createInstance()).apply(game, source); + } + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Stronghold.java b/Mage.Sets/src/mage/sets/Stronghold.java index 0fe9bdb4cad..d07dc5677bf 100644 --- a/Mage.Sets/src/mage/sets/Stronghold.java +++ b/Mage.Sets/src/mage/sets/Stronghold.java @@ -42,6 +42,7 @@ public final class Stronghold extends ExpansionSet { cards.add(new SetCardInfo("Cloud Spirit", 26, Rarity.COMMON, mage.cards.c.CloudSpirit.class)); cards.add(new SetCardInfo("Constant Mists", 104, Rarity.UNCOMMON, mage.cards.c.ConstantMists.class)); cards.add(new SetCardInfo("Contemplation", 4, Rarity.UNCOMMON, mage.cards.c.Contemplation.class)); + cards.add(new SetCardInfo("Contempt", 27, Rarity.COMMON, mage.cards.c.Contempt.class)); cards.add(new SetCardInfo("Conviction", 5, Rarity.COMMON, mage.cards.c.Conviction.class)); cards.add(new SetCardInfo("Convulsing Licid", 77, Rarity.UNCOMMON, mage.cards.c.ConvulsingLicid.class)); cards.add(new SetCardInfo("Corrupting Licid", 54, Rarity.UNCOMMON, mage.cards.c.CorruptingLicid.class)); @@ -122,6 +123,7 @@ public final class Stronghold extends ExpansionSet { cards.add(new SetCardInfo("Shock", 98, Rarity.COMMON, mage.cards.s.Shock.class)); cards.add(new SetCardInfo("Sift", 42, Rarity.COMMON, mage.cards.s.Sift.class)); cards.add(new SetCardInfo("Silver Wyvern", 43, Rarity.RARE, mage.cards.s.SilverWyvern.class)); + cards.add(new SetCardInfo("Skeleton Scavengers", 20, Rarity.RARE, mage.cards.s.SkeletonScavengers.class)); cards.add(new SetCardInfo("Skyshroud Archer", 114, Rarity.COMMON, mage.cards.s.SkyshroudArcher.class)); cards.add(new SetCardInfo("Skyshroud Falcon", 16, Rarity.COMMON, mage.cards.s.SkyshroudFalcon.class)); cards.add(new SetCardInfo("Skyshroud Troopers", 115, Rarity.COMMON, mage.cards.s.SkyshroudTroopers.class));