diff --git a/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java b/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java index da4afed58e1..65fda45f640 100644 --- a/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java +++ b/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java @@ -27,7 +27,7 @@ public class MageRoundPane extends JPanel { private int X_OFFSET = 30; private int Y_OFFSET = 30; - private final Color defaultBackgroundColor = new Color(255, 255, 255, 200); + private final Color defaultBackgroundColor = new Color(141, 130, 112, 200); private Color backgroundColor = defaultBackgroundColor; private final int alpha = 0; private static Map SHADOW_IMAGE_CACHE; diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.form b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.form index f0db80d7055..5065dfbe631 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.form +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.form @@ -201,7 +201,16 @@ - + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java index 266562a8d88..92ea6ce6421 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -27,6 +27,25 @@ */ package mage.client.deckeditor; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import javax.swing.*; +import javax.swing.Timer; +import javax.swing.filechooser.FileFilter; import mage.cards.Card; import mage.cards.Sets; import mage.cards.decks.Deck; @@ -39,8 +58,8 @@ import mage.client.SessionHandler; import mage.client.cards.BigCard; import mage.client.cards.ICardGrid; import mage.client.constants.Constants.DeckEditorMode; -import mage.client.deck.generator.DeckGenerator; import mage.client.deck.generator.DeckGenerator.DeckGeneratorException; +import mage.client.deck.generator.DeckGenerator; import mage.client.dialog.AddLandDialog; import mage.client.plugins.impl.Plugins; import mage.client.util.Event; @@ -52,22 +71,6 @@ import mage.remote.Session; import mage.view.CardView; import mage.view.SimpleCardView; import org.apache.log4j.Logger; - -import javax.swing.*; -import javax.swing.Timer; -import javax.swing.filechooser.FileFilter; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.List; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; - /** * * @author BetaSteward_at_googlemail.com @@ -84,6 +87,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { private int timeout; private Timer countdown; private UpdateDeckTask updateDeckTask; + private int timeToSubmit = -1; /** * Creates new form DeckEditorPanel @@ -160,6 +164,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { this.txtTimeRemaining.setVisible(true); case SIDEBOARDING: this.btnSubmit.setVisible(true); + this.btnSubmitTimer.setVisible(true); if (deck != null) { this.cardSelector.loadSideboard(new ArrayList<>(deck.getSideboard()), this.bigCard); } @@ -187,6 +192,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { case FREE_BUILDING: this.deckArea.setOrientation(/*limitedBuildingOrientation = */false); this.btnSubmit.setVisible(false); + this.btnSubmitTimer.setVisible(false); this.btnAddLand.setVisible(true); this.cardSelector.loadCards(this.bigCard); //this.cardTableSelector.loadCards(this.bigCard); @@ -484,6 +490,11 @@ public class DeckEditorPanel extends javax.swing.JPanel { if (s == 60) { AudioManager.playOnCountdown1(); } + if (timeToSubmit > 0) { + timeToSubmit --; + btnSubmitTimer.setText("Submit (" + timeToSubmit + ")"); + btnSubmitTimer.setToolTipText("Submit your deck in " + timeToSubmit + " seconds!"); + } } private void initComponents() { @@ -501,6 +512,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { btnExit = new javax.swing.JButton(); btnImport = new javax.swing.JButton(); btnSubmit = new javax.swing.JButton(); + btnSubmitTimer = new javax.swing.JButton(); btnAddLand = new javax.swing.JButton(); btnGenDeck = new javax.swing.JButton(); txtTimeRemaining = new javax.swing.JTextField(); @@ -594,6 +606,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { }); btnSubmit.setText("Submit"); + btnSubmitTimer.setToolTipText("Submit your deck now!"); btnSubmit.setName("btnSubmit"); // NOI18N btnSubmit.addActionListener(new java.awt.event.ActionListener() { @Override @@ -602,6 +615,16 @@ public class DeckEditorPanel extends javax.swing.JPanel { } }); + btnSubmitTimer.setText("Submit (60s)"); + btnSubmitTimer.setToolTipText("Submit your deck in one minute!"); + btnSubmitTimer.setName("btnSubmitTimer"); + btnSubmitTimer.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnSubmitTimerActionPerformed(evt); + } + }); + btnAddLand.setText("Add Land"); btnAddLand.setName("btnAddLand"); // NOI18N btnAddLand.addActionListener(new java.awt.event.ActionListener() { @@ -610,7 +633,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { btnAddLandActionPerformed(evt); } }); - + btnGenDeck.setText("Generate"); btnGenDeck.setName("btnGenDeck"); btnGenDeck.addActionListener(new java.awt.event.ActionListener() { @@ -658,7 +681,9 @@ public class DeckEditorPanel extends javax.swing.JPanel { .addContainerGap() .addComponent(btnAddLand) .addContainerGap() - .addComponent(btnSubmit)) + .addComponent(btnSubmit) + .addContainerGap() + .addComponent(btnSubmitTimer)) .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addComponent(txtTimeRemaining)) @@ -682,7 +707,8 @@ public class DeckEditorPanel extends javax.swing.JPanel { .addComponent(btnImport) .addComponent(btnGenDeck) .addComponent(btnAddLand) - .addComponent(btnSubmit)) + .addComponent(btnSubmit) + .addComponent(btnSubmitTimer)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(txtTimeRemaining)) @@ -847,6 +873,26 @@ public class DeckEditorPanel extends javax.swing.JPanel { } }//GEN-LAST:event_btnSubmitActionPerformed + private void btnSubmitTimerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSubmitTimerActionPerformed + + ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5); + timeToSubmit = 60; + this.btnSubmitTimer.setEnabled(false); + + ScheduledFuture scheduledFuture = scheduledExecutorService.schedule(new Callable() { + public Object call() throws Exception { + if (updateDeckTask != null) { + updateDeckTask.cancel(true); + } + + if (SessionHandler.submitDeck(tableId, deck.getDeckCardLists())) { + removeDeckEditor(); + } + return null; + } + }, 60, TimeUnit.SECONDS); + }//GEN-LAST:event_btnSubmitTimerActionPerformed + private void btnAddLandActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnAddLandActionPerformed AddLandDialog addLand = new AddLandDialog(); addLand.showDialog(deck, mode); @@ -867,7 +913,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { } refreshDeck(); } - + // Variables declaration - do not modify//GEN-BEGIN:variables private mage.client.cards.BigCard bigCard; private javax.swing.JButton btnExit; @@ -882,6 +928,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { private javax.swing.JLabel lblDeckName; private javax.swing.JTextField txtDeckName; private javax.swing.JButton btnSubmit; + private javax.swing.JButton btnSubmitTimer; private javax.swing.JButton btnAddLand; private javax.swing.JButton btnGenDeck; private JComponent cardInfoPane; diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 3597ba0b40f..5d7ec878aac 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -916,33 +916,33 @@ public final class GamePanel extends javax.swing.JPanel { private void updateSkipButtons(boolean turn, boolean endOfTurn, boolean nextMain, boolean allTurns, boolean stack, boolean endStepBeforeYourStep) { if (turn) { //F4 - btnSkipToNextTurn.setBorder(new LineBorder(Color.red, BORDER_SIZE)); + btnSkipToNextTurn.setBorder(new LineBorder(Color.orange, BORDER_SIZE)); } else { btnSkipToNextTurn.setBorder(new EmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE)); } if (endOfTurn) { // F5 - btnSkipToEndTurn.setBorder(new LineBorder(Color.red, BORDER_SIZE)); + btnSkipToEndTurn.setBorder(new LineBorder(Color.orange, BORDER_SIZE)); } else { btnSkipToEndTurn.setBorder(new EmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE)); } if (nextMain) { // F7 - btnSkipToNextMain.setBorder(new LineBorder(Color.red, BORDER_SIZE)); + btnSkipToNextMain.setBorder(new LineBorder(Color.orange, BORDER_SIZE)); } else { btnSkipToNextMain.setBorder(new EmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE)); } if (stack) { // F8 - btnSkipStack.setBorder(new LineBorder(Color.red, BORDER_SIZE)); + btnSkipStack.setBorder(new LineBorder(Color.orange, BORDER_SIZE)); } else { btnSkipStack.setBorder(new EmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE)); } if (allTurns) { // F9 - btnSkipToYourTurn.setBorder(new LineBorder(Color.red, BORDER_SIZE)); + btnSkipToYourTurn.setBorder(new LineBorder(Color.orange, BORDER_SIZE)); } else { btnSkipToYourTurn.setBorder(new EmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE)); } if (endStepBeforeYourStep) { // F11 - btnSkipToEndStepBeforeYourTurn.setBorder(new LineBorder(Color.red, BORDER_SIZE)); + btnSkipToEndStepBeforeYourTurn.setBorder(new LineBorder(Color.orange, BORDER_SIZE)); } else { btnSkipToEndStepBeforeYourTurn.setBorder(new EmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE)); } diff --git a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java index 67484a2f361..fc494cdbe26 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java @@ -113,8 +113,8 @@ public class PlayerPanelExt extends javax.swing.JPanel { private static final Border GREEN_BORDER = new LineBorder(Color.green, 3); private static final Border RED_BORDER = new LineBorder(Color.red, 2); private static final Border EMPTY_BORDER = BorderFactory.createEmptyBorder(0, 0, 0, 0); - private final Color greenBackgroundColor = new Color(180, 255, 180, 200); - private final Color deadBackgroundColor = new Color(200, 180, 180, 200); + private final Color greenBackgroundColor = new Color(206, 192, 174, 200); + private final Color deadBackgroundColor = new Color(131, 94, 83, 200); private int avatarId = -1; private String flagName; @@ -430,7 +430,7 @@ public class PlayerPanelExt extends javax.swing.JPanel { // Poison count poisonLabel.setText("0"); - r = new Rectangle(14, 14); + r = new Rectangle(18, 18); poisonLabel.setToolTipText("Poison"); Image imagePoison = ImageHelper.getImageFromResources("/info/poison.png"); BufferedImage resizedPoison = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(imagePoison, BufferedImage.TYPE_INT_ARGB), r); @@ -650,8 +650,7 @@ public class PlayerPanelExt extends javax.swing.JPanel { .addGap(9) .addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING) .addGroup(gl_panelBackground.createSequentialGroup() - .addGap(3) - .addComponent(poison, GroupLayout.PREFERRED_SIZE, 14, GroupLayout.PREFERRED_SIZE)) + .addComponent(poison, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE)) .addGroup(gl_panelBackground.createSequentialGroup() .addGap(2) .addComponent(btnWhiteMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE)) @@ -720,7 +719,7 @@ public class PlayerPanelExt extends javax.swing.JPanel { .addComponent(avatar, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 80, Short.MAX_VALUE)) // .addGroup(gl_panelBackground.createSequentialGroup() // .addComponent(avatarFlag, GroupLayout.PREFERRED_SIZE, 16, GroupLayout.PREFERRED_SIZE)) - .addGap(14)) + .addGap(8)) .addGroup(gl_panelBackground.createSequentialGroup() .addGap(6) .addComponent(zonesPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE) @@ -734,7 +733,7 @@ public class PlayerPanelExt extends javax.swing.JPanel { .addPreferredGap(ComponentPlacement.RELATED) .addComponent(btnPlayer) .addComponent(timerLabel) - .addGap(1) + .addGap(2) // Life & Hand .addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING) .addGroup(gl_panelBackground.createSequentialGroup() @@ -749,15 +748,15 @@ public class PlayerPanelExt extends javax.swing.JPanel { // Poison .addGroup(gl_panelBackground.createParallelGroup(Alignment.LEADING) .addGroup(gl_panelBackground.createSequentialGroup() - .addGap(4) - .addComponent(poison, GroupLayout.PREFERRED_SIZE, 14, GroupLayout.PREFERRED_SIZE) + .addGap(2) + .addComponent(poison, GroupLayout.PREFERRED_SIZE, 18, GroupLayout.PREFERRED_SIZE) .addGap(4) .addComponent(btnWhiteMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE) .addGap(2) .addComponent(btnBlueMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE) .addGap(2) .addComponent(btnBlackMana, GroupLayout.PREFERRED_SIZE, 15, GroupLayout.PREFERRED_SIZE) - .addGap(5) + .addGap(3) .addComponent(grave, GroupLayout.PREFERRED_SIZE, 21, GroupLayout.PREFERRED_SIZE) ) .addGroup(gl_panelBackground.createSequentialGroup() diff --git a/Mage.Client/src/main/resources/buttons/blank.png b/Mage.Client/src/main/resources/buttons/blank.png index 3dcfd353236..d61587dfe5d 100644 Binary files a/Mage.Client/src/main/resources/buttons/blank.png and b/Mage.Client/src/main/resources/buttons/blank.png differ diff --git a/Mage.Client/src/main/resources/buttons/cancel_skip.png b/Mage.Client/src/main/resources/buttons/cancel_skip.png index c418d5d63e7..30755df9317 100644 Binary files a/Mage.Client/src/main/resources/buttons/cancel_skip.png and b/Mage.Client/src/main/resources/buttons/cancel_skip.png differ diff --git a/Mage.Client/src/main/resources/buttons/concede.png b/Mage.Client/src/main/resources/buttons/concede.png index 4f56f52b942..f0795a00cac 100644 Binary files a/Mage.Client/src/main/resources/buttons/concede.png and b/Mage.Client/src/main/resources/buttons/concede.png differ diff --git a/Mage.Client/src/main/resources/buttons/skip_all.png b/Mage.Client/src/main/resources/buttons/skip_all.png index 9af7be0c783..53c01378ade 100644 Binary files a/Mage.Client/src/main/resources/buttons/skip_all.png and b/Mage.Client/src/main/resources/buttons/skip_all.png differ diff --git a/Mage.Client/src/main/resources/buttons/skip_stack.png b/Mage.Client/src/main/resources/buttons/skip_stack.png index 40597cfb64f..06816b1f393 100644 Binary files a/Mage.Client/src/main/resources/buttons/skip_stack.png and b/Mage.Client/src/main/resources/buttons/skip_stack.png differ diff --git a/Mage.Client/src/main/resources/buttons/skip_to_end.png b/Mage.Client/src/main/resources/buttons/skip_to_end.png index 7325a90b1f1..86df3bcf7c4 100644 Binary files a/Mage.Client/src/main/resources/buttons/skip_to_end.png and b/Mage.Client/src/main/resources/buttons/skip_to_end.png differ diff --git a/Mage.Client/src/main/resources/buttons/skip_to_main.png b/Mage.Client/src/main/resources/buttons/skip_to_main.png index 8aec79da181..0d1b2bf1155 100644 Binary files a/Mage.Client/src/main/resources/buttons/skip_to_main.png and b/Mage.Client/src/main/resources/buttons/skip_to_main.png differ diff --git a/Mage.Client/src/main/resources/buttons/skip_to_previous_end.png b/Mage.Client/src/main/resources/buttons/skip_to_previous_end.png index f5f737d8f43..2e93fd55bbb 100644 Binary files a/Mage.Client/src/main/resources/buttons/skip_to_previous_end.png and b/Mage.Client/src/main/resources/buttons/skip_to_previous_end.png differ diff --git a/Mage.Client/src/main/resources/buttons/skip_turn.png b/Mage.Client/src/main/resources/buttons/skip_turn.png index 4c35eda03e4..c7c3bfe393b 100644 Binary files a/Mage.Client/src/main/resources/buttons/skip_turn.png and b/Mage.Client/src/main/resources/buttons/skip_turn.png differ diff --git a/Mage.Client/src/main/resources/buttons/stop_watching.png b/Mage.Client/src/main/resources/buttons/stop_watching.png index 717de1f96ee..c45a5f639fd 100644 Binary files a/Mage.Client/src/main/resources/buttons/stop_watching.png and b/Mage.Client/src/main/resources/buttons/stop_watching.png differ diff --git a/Mage.Client/src/main/resources/buttons/switch_hands.png b/Mage.Client/src/main/resources/buttons/switch_hands.png index 9c1dab456db..b38e20b9295 100644 Binary files a/Mage.Client/src/main/resources/buttons/switch_hands.png and b/Mage.Client/src/main/resources/buttons/switch_hands.png differ diff --git a/Mage.Client/src/main/resources/info/command_zone.png b/Mage.Client/src/main/resources/info/command_zone.png index a992c555d50..33dbba8c5d5 100644 Binary files a/Mage.Client/src/main/resources/info/command_zone.png and b/Mage.Client/src/main/resources/info/command_zone.png differ diff --git a/Mage.Client/src/main/resources/info/exile.png b/Mage.Client/src/main/resources/info/exile.png index 6f0fe92cc39..1cf37dadf1e 100644 Binary files a/Mage.Client/src/main/resources/info/exile.png and b/Mage.Client/src/main/resources/info/exile.png differ diff --git a/Mage.Client/src/main/resources/info/grave.png b/Mage.Client/src/main/resources/info/grave.png index b2678b09ca3..71ea1f4b5ca 100644 Binary files a/Mage.Client/src/main/resources/info/grave.png and b/Mage.Client/src/main/resources/info/grave.png differ diff --git a/Mage.Client/src/main/resources/info/hand.png b/Mage.Client/src/main/resources/info/hand.png index 04482b29c74..59d89555160 100644 Binary files a/Mage.Client/src/main/resources/info/hand.png and b/Mage.Client/src/main/resources/info/hand.png differ diff --git a/Mage.Client/src/main/resources/info/library.png b/Mage.Client/src/main/resources/info/library.png index 9cb19eb172b..f799e808aa3 100644 Binary files a/Mage.Client/src/main/resources/info/library.png and b/Mage.Client/src/main/resources/info/library.png differ diff --git a/Mage.Client/src/main/resources/info/life.png b/Mage.Client/src/main/resources/info/life.png index a3b5045ff28..e4c8e45f96e 100644 Binary files a/Mage.Client/src/main/resources/info/life.png and b/Mage.Client/src/main/resources/info/life.png differ diff --git a/Mage.Client/src/main/resources/info/poison.png b/Mage.Client/src/main/resources/info/poison.png index 888091bd1a8..181a479be6d 100644 Binary files a/Mage.Client/src/main/resources/info/poison.png and b/Mage.Client/src/main/resources/info/poison.png differ diff --git a/Mage.Common/src/mage/utils/MageVersion.java b/Mage.Common/src/mage/utils/MageVersion.java index f851090a8fd..ad4b3c5ef3e 100644 --- a/Mage.Common/src/mage/utils/MageVersion.java +++ b/Mage.Common/src/mage/utils/MageVersion.java @@ -41,7 +41,7 @@ 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 = 15; - public final static String MAGE_VERSION_MINOR_PATCH = "v2"; + public final static String MAGE_VERSION_MINOR_PATCH = "v4"; public final static String MAGE_VERSION_INFO = ""; private final int major; diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 2e0414ecb8c..9accefe8ed5 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -30,7 +30,6 @@ package mage.view; import java.util.ArrayList; import java.util.List; import java.util.UUID; - import mage.MageObject; import mage.ObjectColor; import mage.abilities.Mode; @@ -55,7 +54,6 @@ import mage.game.stack.Spell; import mage.game.stack.StackAbility; import mage.target.Target; import mage.target.Targets; -import org.apache.log4j.Logger; /** * @author BetaSteward_at_googlemail.com @@ -81,7 +79,7 @@ public class CardView extends SimpleCardView { protected List manaCost; protected int convertedManaCost; protected Rarity rarity; - + protected MageObjectType mageObjectType = MageObjectType.NULL; protected boolean isAbility; @@ -323,7 +321,8 @@ public class CardView extends SimpleCardView { this.mageObjectType = MageObjectType.SPELL; Spell spell = (Spell) card; for (SpellAbility spellAbility : spell.getSpellAbilities()) { - for (Mode mode : spellAbility.getModes().getSelectedModes()) { + for (UUID modeId : spellAbility.getModes().getSelectedModes()) { + Mode mode = spellAbility.getModes().get(modeId); if (mode.getTargets().size() > 0) { setTargets(spellAbility.getTargets()); } @@ -331,18 +330,19 @@ public class CardView extends SimpleCardView { } // show for modal spell, which mode was choosen if (spell.getSpellAbility().isModal()) { - for (Mode mode : spell.getSpellAbility().getModes().getSelectedModes()) { + for (UUID modeId : spell.getSpellAbility().getModes().getSelectedModes()) { + Mode mode = spell.getSpellAbility().getModes().get(modeId); this.rules.add("Chosen mode: " + mode.getEffects().getText(mode) + ""); } } } - + // Frame color this.frameColor = card.getFrameColor(game); // Frame style this.frameStyle = card.getFrameStyle(); - + // Get starting loyalty this.startingLoyalty = "" + card.getStartingLoyalty(); } @@ -355,7 +355,7 @@ public class CardView extends SimpleCardView { this.mageObjectType = MageObjectType.PERMANENT; this.power = Integer.toString(object.getPower().getValue()); this.toughness = Integer.toString(object.getToughness().getValue()); - this.loyalty = Integer.toString(((Permanent) object).getCounters((Game)null).getCount(CounterType.LOYALTY)); + this.loyalty = Integer.toString(((Permanent) object).getCounters((Game) null).getCount(CounterType.LOYALTY)); } else { this.power = object.getPower().toString(); this.toughness = object.getToughness().toString(); @@ -488,7 +488,7 @@ public class CardView extends SimpleCardView { this.rarity = Rarity.NA; this.type = token.getTokenType(); this.tokenDescriptor = token.getTokenDescriptor(); - this.tokenSetCode = token.getOriginalExpansionSetCode(); + this.tokenSetCode = token.getOriginalExpansionSetCode(); } protected final void setTargets(Targets targets) { @@ -547,7 +547,7 @@ public class CardView extends SimpleCardView { public String getLoyalty() { return loyalty; } - + public String getStartingLoyalty() { return startingLoyalty; } @@ -567,7 +567,7 @@ public class CardView extends SimpleCardView { public ObjectColor getColor() { return color; } - + public ObjectColor getFrameColor() { return frameColor; } @@ -807,4 +807,3 @@ public class CardView extends SimpleCardView { } } - diff --git a/Mage.Common/src/mage/view/StackAbilityView.java b/Mage.Common/src/mage/view/StackAbilityView.java index 9b195d1b5bb..1c5c6914d52 100644 --- a/Mage.Common/src/mage/view/StackAbilityView.java +++ b/Mage.Common/src/mage/view/StackAbilityView.java @@ -99,7 +99,8 @@ public class StackAbilityView extends CardView { private void updateTargets(Game game, StackAbility ability) { List names = new ArrayList<>(); - for (Mode mode : ability.getModes().getSelectedModes()) { + for (UUID modeId : ability.getModes().getSelectedModes()) { + Mode mode = ability.getModes().get(modeId); if (mode.getTargets().size() > 0) { setTargets(mode.getTargets()); } else { @@ -132,7 +133,8 @@ public class StackAbilityView extends CardView { // show for modal ability, which mode was choosen if (ability.isModal()) { Modes modes = ability.getModes(); - for (Mode mode : modes.getSelectedModes()) { + for (UUID modeId : modes.getSelectedModes()) { + Mode mode = modes.get(modeId); this.rules.add("Chosen mode: " + mode.getEffects().getText(mode) + ""); } } 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 0d1a60d29dd..8f2d515fe35 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 @@ -1516,7 +1516,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { //TODO: improve this; AvailableMode: for (Mode mode : modes.getAvailableModes(source, game)) { - for (Mode selectedMode : modes.getSelectedModes()) { + for (UUID selectedModeId : modes.getSelectedModes()) { + Mode selectedMode = modes.get(selectedModeId); if (selectedMode.getId().equals(mode.getId())) { continue AvailableMode; } diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 7d619864c53..03ca681e47f 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -730,11 +730,14 @@ public class HumanPlayer extends PlayerImpl { } private boolean checkPassStep(Game game) { - if (playerId.equals(game.getActivePlayerId())) { - return !this.getUserData().getUserSkipPrioritySteps().getYourTurn().isPhaseStepSet(game.getStep().getType()); - } else { - return !this.getUserData().getUserSkipPrioritySteps().getOpponentTurn().isPhaseStepSet(game.getStep().getType()); + if (game.getStep() != null) { + if (playerId.equals(game.getActivePlayerId())) { + return !this.getUserData().getUserSkipPrioritySteps().getYourTurn().isPhaseStepSet(game.getStep().getType()); + } else { + return !this.getUserData().getUserSkipPrioritySteps().getOpponentTurn().isPhaseStepSet(game.getStep().getType()); + } } + return true; } @Override @@ -1323,7 +1326,8 @@ public class HumanPlayer extends PlayerImpl { AvailableModes: for (Mode mode : modes.getAvailableModes(source, game)) { int timesSelected = 0; - for (Mode selectedMode : modes.getSelectedModes()) { + for (UUID selectedModeId : modes.getSelectedModes()) { + Mode selectedMode = modes.get(selectedModeId); if (mode.getId().equals(selectedMode.getId())) { if (modes.isEachModeMoreThanOnce()) { timesSelected++; diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 608510af330..74a28df7ac2 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -102,6 +102,7 @@ + diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml index 606359499cf..99c5047d52c 100644 --- a/Mage.Server/release/config/config.xml +++ b/Mage.Server/release/config/config.xml @@ -99,6 +99,7 @@ + diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index 266ef9164b4..4d99b36126b 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -567,7 +567,7 @@ public class TableController { String creator = null; StringBuilder opponent = new StringBuilder(); for (Entry entry : userPlayerMap.entrySet()) { // no AI players - if (!match.getPlayer(entry.getValue()).hasQuit()) { + if (match.getPlayer(entry.getValue()) != null && !match.getPlayer(entry.getValue()).hasQuit()) { User user = UserManager.getInstance().getUser(entry.getKey()); if (user != null) { user.ccGameStarted(match.getGame().getId(), entry.getValue()); diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/ZadaHedronGrinder.java b/Mage.Sets/src/mage/sets/battleforzendikar/ZadaHedronGrinder.java index 129686fc335..557f05bd6f1 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/ZadaHedronGrinder.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/ZadaHedronGrinder.java @@ -104,7 +104,8 @@ class ZadaHedronGrinderTriggeredAbility extends TriggeredAbilityImpl { if (isControlledInstantOrSorcery(spell)) { boolean targetsSource = false; for (Ability ability : spell.getSpellAbilities()) { - for (Mode mode : ability.getModes().getSelectedModes()) { + for (UUID modeId : ability.getModes().getSelectedModes()) { + Mode mode = ability.getModes().get(modeId); for (Target target : mode.getTargets()) { if (!target.isNotTarget()) { for (UUID targetId : target.getTargets()) { @@ -167,7 +168,8 @@ class ZadaHedronGrinderEffect extends OneShotEffect { Target usedTarget = null; setUsedTarget: for (Ability ability : spell.getSpellAbilities()) { - for (Mode mode : ability.getModes().getSelectedModes()) { + for (UUID modeId : ability.getModes().getSelectedModes()) { + Mode mode = ability.getModes().get(modeId); for (Target target : mode.getTargets()) { if (!target.isNotTarget() && target.getFirstTarget().equals(source.getSourceId())) { usedTarget = target.copy(); @@ -185,7 +187,8 @@ class ZadaHedronGrinderEffect extends OneShotEffect { Spell copy = spell.copySpell(source.getControllerId()); game.getStack().push(copy); setTarget: - for (Mode mode : copy.getSpellAbility().getModes().getSelectedModes()) { + for (UUID modeId : copy.getSpellAbility().getModes().getSelectedModes()) { + Mode mode = copy.getSpellAbility().getModes().get(modeId); for (Target target : mode.getTargets()) { if (target.getClass().equals(usedTarget.getClass())) { target.clearChosen(); // For targets with Max > 1 we need to clear before the text is comapred diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfLifesWeb.java b/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfLifesWeb.java index 1bae38bda5e..a05533cee27 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfLifesWeb.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/MyojinOfLifesWeb.java @@ -28,8 +28,6 @@ package mage.sets.championsofkamigawa; import java.util.UUID; - -import mage.constants.*; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; @@ -40,15 +38,17 @@ import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.common.PutOntoBattlefieldTargetEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; +import mage.cards.CardsImpl; +import mage.constants.*; import mage.counters.CounterType; -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.filter.predicate.other.OwnerPredicate; +import mage.filter.common.FilterCreatureCard; +import mage.game.Game; +import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.watchers.common.CastFromHandWatcher; @@ -57,12 +57,6 @@ import mage.watchers.common.CastFromHandWatcher; */ public class MyojinOfLifesWeb extends CardImpl { - private static final FilterCard filter = new FilterCard("any number of creature cards from your hand"); - static { - filter.add(new CardTypePredicate(CardType.CREATURE)); - filter.add(new OwnerPredicate(TargetController.YOU)); - } - public MyojinOfLifesWeb(UUID ownerId) { super(ownerId, 229, "Myojin of Life's Web", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{6}{G}{G}{G}"); this.expansionSetCode = "CHK"; @@ -79,10 +73,10 @@ public class MyojinOfLifesWeb extends CardImpl { // Myojin of Life's Web is indestructible as long as it has a divinity counter on it. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield), - new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it"))); + new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it"))); // Remove a divinity counter from Myojin of Life's Web: Put any number of creature cards from your hand onto the battlefield. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutOntoBattlefieldTargetEffect(false), new RemoveCountersSourceCost(CounterType.DIVINITY.createInstance())); - ability.addTarget(new TargetCardInHand(0, Integer.MAX_VALUE, filter)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MyojinOfLifesWebPutCreatureOnBattlefieldEffect(), new RemoveCountersSourceCost(CounterType.DIVINITY.createInstance())); + this.addAbility(ability); } @@ -95,3 +89,35 @@ public class MyojinOfLifesWeb extends CardImpl { return new MyojinOfLifesWeb(this); } } + +class MyojinOfLifesWebPutCreatureOnBattlefieldEffect extends OneShotEffect { + + public MyojinOfLifesWebPutCreatureOnBattlefieldEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "Put any number of creature cards from your hand onto the battlefield"; + } + + public MyojinOfLifesWebPutCreatureOnBattlefieldEffect(final MyojinOfLifesWebPutCreatureOnBattlefieldEffect effect) { + super(effect); + } + + @Override + public MyojinOfLifesWebPutCreatureOnBattlefieldEffect copy() { + return new MyojinOfLifesWebPutCreatureOnBattlefieldEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + + TargetCardInHand target = new TargetCardInHand(0, Integer.MAX_VALUE, new FilterCreatureCard("creature cards from your hand to put onto the battlefield")); + if (controller.choose(Outcome.PutCreatureInPlay, target, source.getSourceId(), game)) { + return controller.moveCards(new CardsImpl(target.getTargets()).getCards(game), + Zone.BATTLEFIELD, source, game, false, false, false, null); + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/chronicles/GoblinArtisans.java b/Mage.Sets/src/mage/sets/chronicles/GoblinArtisans.java index a255033f825..271326e7b58 100644 --- a/Mage.Sets/src/mage/sets/chronicles/GoblinArtisans.java +++ b/Mage.Sets/src/mage/sets/chronicles/GoblinArtisans.java @@ -70,7 +70,7 @@ public class GoblinArtisans extends CardImpl { // {tap}: Flip a coin. If you win the flip, draw a card. If you lose the flip, counter target artifact spell you control that isn't the target of an ability from another creature named Goblin Artisans. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GoblinArtisansEffect(), new TapSourceCost())); - + } public GoblinArtisans(final GoblinArtisans card) { @@ -83,15 +83,14 @@ public class GoblinArtisans extends CardImpl { } } - class GoblinArtisansEffect extends OneShotEffect { private static final FilterPermanent filter = new FilterPermanent("permanent named Goblin Artisans"); - + static { filter.add(new NamePredicate("Goblin Artisans")); } - + public GoblinArtisansEffect() { super(Outcome.Damage); staticText = "Flip a coin. If you win the flip, draw a card. If you lose the flip, counter target artifact spell you control that isn't the target of an ability from another creature named Goblin Artisans."; @@ -109,37 +108,38 @@ class GoblinArtisansEffect extends OneShotEffect { controller.drawCards(1, game); } else { List artifacts = game.getBattlefield().getActivePermanents(new FilterControlledArtifactPermanent(), source.getControllerId(), game); - if (artifacts.isEmpty()){//Don't even bother if there is no artifact to 'counter'/sacrifice + if (artifacts.isEmpty()) {//Don't even bother if there is no artifact to 'counter'/sacrifice return true; - } - + } + filter.add(Predicates.not(new PermanentIdPredicate(source.getSourceId()))); - //removed the activating instance of Artisans, btw, wasn't that filter declared as static final? How come I can do this here? :) - List list = game.getBattlefield().getAllActivePermanents(filter, game); - for (Permanent perm : list){ // should I limit below for a particular kind of ability? Going for the most general, it's unlikely there'll be any other artisans anyway, so not concerned about efficiency :p - for (Ability abil : perm.getAbilities(game)){//below is copied from TargetsPermanentPredicate, but why only "selectedModes"? Shouldnt be more general as well? - for (Mode mode : abil.getModes().getSelectedModes()){ + //removed the activating instance of Artisans, btw, wasn't that filter declared as static final? How come I can do this here? :) + List list = game.getBattlefield().getAllActivePermanents(filter, game); + for (Permanent perm : list) { // should I limit below for a particular kind of ability? Going for the most general, it's unlikely there'll be any other artisans anyway, so not concerned about efficiency :p + for (Ability abil : perm.getAbilities(game)) {//below is copied from TargetsPermanentPredicate, but why only "selectedModes"? Shouldnt be more general as well? + for (UUID modeId : abil.getModes().getSelectedModes()) { + Mode mode = abil.getModes().get(modeId); for (Target target : mode.getTargets()) { - for (UUID targetId : target.getTargets()) { - artifacts.remove(game.getPermanentOrLKIBattlefield(targetId)); - }// we could - }// remove this - }//closing bracers - }// pyramid, if it's bothering anyone - } //they are all one-liners after all :) - if (!artifacts.isEmpty()){ - Cards cards=new CardsImpl(); - for (Permanent perm : artifacts){ + for (UUID targetId : target.getTargets()) { + artifacts.remove(game.getPermanentOrLKIBattlefield(targetId)); + }// we could + }// remove this + }//closing bracers + }// pyramid, if it's bothering anyone + } //they are all one-liners after all :) + if (!artifacts.isEmpty()) { + Cards cards = new CardsImpl(); + for (Permanent perm : artifacts) { cards.add(perm.getId()); } - TargetCard target = new TargetCard(Zone.BATTLEFIELD, new FilterCard()); + TargetCard target = new TargetCard(Zone.BATTLEFIELD, new FilterCard()); controller.choose(Outcome.Sacrifice, cards, target, game); - game.getPermanent(target.getFirstTarget()).sacrifice(source.getSourceId(), game); - } + game.getPermanent(target.getFirstTarget()).sacrifice(source.getSourceId(), game); + } return true; - } } - + } + return false; } diff --git a/Mage.Sets/src/mage/sets/commander2013/JelevaNephaliasScourge.java b/Mage.Sets/src/mage/sets/commander2013/JelevaNephaliasScourge.java index 40657270108..49573dd952c 100644 --- a/Mage.Sets/src/mage/sets/commander2013/JelevaNephaliasScourge.java +++ b/Mage.Sets/src/mage/sets/commander2013/JelevaNephaliasScourge.java @@ -120,7 +120,7 @@ class JelevaNephaliasScourgeEffect extends OneShotEffect { for (int i = 0; i < cardsToExile; i++) { Card card = player.getLibrary().removeFromTop(game); if (card != null) { - card.moveToExile(CardUtil.getCardExileZoneId(game, source), sourceCard.getName(), source.getSourceId(), game); + card.moveToExile(CardUtil.getCardExileZoneId(game, source), sourceCard.getIdName(), source.getSourceId(), game); } } } diff --git a/Mage.Sets/src/mage/sets/conspiracytakethecrown/LeovoldEmissaryOfTrest.java b/Mage.Sets/src/mage/sets/conspiracytakethecrown/LeovoldEmissaryOfTrest.java index 7f372c4baf4..179cf7c0b4a 100644 --- a/Mage.Sets/src/mage/sets/conspiracytakethecrown/LeovoldEmissaryOfTrest.java +++ b/Mage.Sets/src/mage/sets/conspiracytakethecrown/LeovoldEmissaryOfTrest.java @@ -27,7 +27,6 @@ */ package mage.sets.conspiracytakethecrown; -import java.util.HashSet; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; @@ -41,8 +40,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.watchers.Watcher; - +import mage.watchers.common.CardsAmountDrawnThisTurnWatcher; /** * @@ -60,7 +58,7 @@ public class LeovoldEmissaryOfTrest extends CardImpl { this.toughness = new MageInt(3); // Each opponent can't draw more than one card each turn. (Based on SpiritOfTheLabyrinth) - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LeovoldEmissaryOfTrestEffect()), new LeovoldEmissaryOfTrestWatcher()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LeovoldEmissaryOfTrestEffect()), new CardsAmountDrawnThisTurnWatcher()); // Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. this.addAbility(new LeovoldEmissaryOfTrestTriggeredAbility()); @@ -76,47 +74,6 @@ public class LeovoldEmissaryOfTrest extends CardImpl { } } - -class LeovoldEmissaryOfTrestWatcher extends Watcher { - - private final HashSet playersThatDrewCard; - - public LeovoldEmissaryOfTrestWatcher() { - super("DrewCard", WatcherScope.GAME); - this.playersThatDrewCard = new HashSet<>(); - } - - public LeovoldEmissaryOfTrestWatcher(final LeovoldEmissaryOfTrestWatcher watcher) { - super(watcher); - this.playersThatDrewCard = new HashSet<>(); - playersThatDrewCard.addAll(watcher.playersThatDrewCard); - } - - @Override - public LeovoldEmissaryOfTrestWatcher copy() { - return new LeovoldEmissaryOfTrestWatcher(this); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DREW_CARD ) { - playersThatDrewCard.add(event.getPlayerId()); - - } - } - - @Override - public void reset() { - super.reset(); - playersThatDrewCard.clear(); - } - - public boolean hasPlayerDrewCardThisTurn(UUID playerId) { - return playersThatDrewCard.contains(playerId); - } - -} - class LeovoldEmissaryOfTrestEffect extends ContinuousRuleModifyingEffectImpl { public LeovoldEmissaryOfTrestEffect() { @@ -133,11 +90,6 @@ class LeovoldEmissaryOfTrestEffect extends ContinuousRuleModifyingEffectImpl { return new LeovoldEmissaryOfTrestEffect(this); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.DRAW_CARD; @@ -145,10 +97,9 @@ class LeovoldEmissaryOfTrestEffect extends ContinuousRuleModifyingEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - LeovoldEmissaryOfTrestWatcher watcher = (LeovoldEmissaryOfTrestWatcher) game.getState().getWatchers().get("DrewCard"); - + CardsAmountDrawnThisTurnWatcher watcher = (CardsAmountDrawnThisTurnWatcher) game.getState().getWatchers().get(CardsAmountDrawnThisTurnWatcher.BASIC_KEY); Player controller = game.getPlayer(source.getControllerId()); - return watcher != null && controller != null && watcher.hasPlayerDrewCardThisTurn(event.getPlayerId()) + return watcher != null && controller != null && watcher.getAmountCardsDrawn(event.getPlayerId()) >= 1 && game.isOpponent(controller, event.getPlayerId()); } @@ -178,9 +129,10 @@ class LeovoldEmissaryOfTrestTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { Player controller = game.getPlayer(this.getControllerId()); Player targetter = game.getPlayer(event.getPlayerId()); - if (controller != null && targetter != null && !controller.getId().equals(targetter.getId())) { + if (controller != null && targetter != null + && game.isOpponent(controller, targetter.getId())) { if (event.getTargetId().equals(controller.getId())) { - return true; + return true; // Player was targeted } Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); if (permanent != null && this.getControllerId().equals(permanent.getControllerId())) { @@ -192,6 +144,6 @@ class LeovoldEmissaryOfTrestTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card."; + return "Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, " + super.getRule(); } } diff --git a/Mage.Sets/src/mage/sets/conspiracytakethecrown/SanctumPrelate.java b/Mage.Sets/src/mage/sets/conspiracytakethecrown/SanctumPrelate.java index 3b8ce2490d0..decc2fb635e 100644 --- a/Mage.Sets/src/mage/sets/conspiracytakethecrown/SanctumPrelate.java +++ b/Mage.Sets/src/mage/sets/conspiracytakethecrown/SanctumPrelate.java @@ -43,6 +43,7 @@ import mage.choices.ChoiceImpl; import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.Player; import org.apache.log4j.Logger; @@ -95,6 +96,12 @@ class ChooseNumberEffect extends OneShotEffect { int numberChoice = controller.announceXMana(0, Integer.MAX_VALUE, "Choose a number. Noncreature spells with the chosen converted mana cost can't be cast", game, source); game.getState().setValue(source.getSourceId().toString(), numberChoice); + + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + permanent.addInfo("chosen players", "Chosen Number: "+ numberChoice +"", game); + + game.informPlayers(permanent.getLogName() + ", chosen number: "+numberChoice); + return true; } diff --git a/Mage.Sets/src/mage/sets/conspiracytakethecrown/SelvalaHeartOfTheWilds.java b/Mage.Sets/src/mage/sets/conspiracytakethecrown/SelvalaHeartOfTheWilds.java index d092cf4b3e7..6aff8f6068c 100644 --- a/Mage.Sets/src/mage/sets/conspiracytakethecrown/SelvalaHeartOfTheWilds.java +++ b/Mage.Sets/src/mage/sets/conspiracytakethecrown/SelvalaHeartOfTheWilds.java @@ -33,13 +33,13 @@ import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; import mage.constants.*; -import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.ObjectSourcePlayer; @@ -48,7 +48,6 @@ import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; /** * @@ -56,8 +55,8 @@ import mage.target.targetpointer.FixedTarget; */ public class SelvalaHeartOfTheWilds extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature"); + static { filter.add(new AnotherPredicate()); filter.add(new GreatestPowerPredicate()); @@ -75,12 +74,14 @@ public class SelvalaHeartOfTheWilds extends CardImpl { this.toughness = new MageInt(3); // Whenever another creature enters the battlefield, its controller may draw a card if its power is greater than each other creature's power. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new SelvalaHeartOfTheWildsEffect(), filter, false, SetTargetPointer.PERMANENT, rule)); // {G}, {T}: Add X mana in any combination of colors to your mana pool, where X is the greatest power among creatures you control. - this.addAbility(new DynamicManaAbility(new Mana(0,0,0,0,0,0,1, 0), new GreatestPowerYouControlValue(), new TapSourceCost(), - "Add X mana in any combination of colors to your mana pool, where X is the number of creatures with defender you control.")); + Ability ability = new DynamicManaAbility(new Mana(0, 0, 0, 0, 0, 0, 1, 0), new GreatestPowerYouControlValue(), new ManaCostsImpl<>("{G}"), + "Add X mana in any combination of colors to your mana pool, where X is the greatest power among creatures you control."); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } public SelvalaHeartOfTheWilds(final SelvalaHeartOfTheWilds card) { @@ -112,12 +113,12 @@ class SelvalaHeartOfTheWildsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); - if(permanent == null){ - permanent = (Permanent)game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD); + if (permanent == null) { + permanent = (Permanent) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD); } if (permanent != null) { Player cardowner = game.getPlayer(permanent.getControllerId()); - if(cardowner.chooseUse(Outcome.DrawCard, "Would you like to draw a card?", source, game)){ + if (cardowner.chooseUse(Outcome.DrawCard, "Would you like to draw a card?", source, game)) { cardowner.drawCards(1, game); } } @@ -125,18 +126,17 @@ class SelvalaHeartOfTheWildsEffect extends OneShotEffect { } } - class GreatestPowerPredicate implements ObjectSourcePlayerPredicate> { @Override public boolean apply(ObjectSourcePlayer input, Game game) { int pow = input.getObject().getPower().getValue(); - for (UUID id :game.getPlayerList()){ + for (UUID id : game.getPlayerList()) { Player player = game.getPlayer(id); if (player != null) { for (Permanent p : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), id, game)) { - if(p.getPower().getValue() >= pow && !p.equals(input.getObject())){ + if (p.getPower().getValue() >= pow && !p.equals(input.getObject())) { return false; //we found something with equal/more power } } @@ -144,6 +144,7 @@ class GreatestPowerPredicate implements ObjectSourcePlayerPredicate 1) { - numberToMove = controller.getAmount(0, numberOfCounters, "How many +1/+1 counters do you want to move?", game); - } - if (numberToMove > 0) { - fromPermanent.removeCounters(CounterType.P1P1.createInstance(numberToMove), game); - sourceObject.addCounters(CounterType.P1P1.createInstance(numberToMove), game); + target.setNotTarget(true); + if (target.choose(Outcome.Neutral, source.getControllerId(), source.getSourceId(), game)) { + Permanent fromPermanent = game.getPermanent(target.getFirstTarget()); + if (fromPermanent != null) { + int numberOfCounters = fromPermanent.getCounters(game).getCount(CounterType.P1P1); + int numberToMove = 1; + if (numberOfCounters > 1) { + numberToMove = controller.getAmount(0, numberOfCounters, "How many +1/+1 counters do you want to move?", game); + } + if (numberToMove > 0) { + fromPermanent.removeCounters(CounterType.P1P1.createInstance(numberToMove), game); + sourceObject.addCounters(CounterType.P1P1.createInstance(numberToMove), game); + } } } } else { diff --git a/Mage.Sets/src/mage/sets/kaladesh/BomatCourier.java b/Mage.Sets/src/mage/sets/kaladesh/BomatCourier.java index 666b27c01c6..88053bc2356 100644 --- a/Mage.Sets/src/mage/sets/kaladesh/BomatCourier.java +++ b/Mage.Sets/src/mage/sets/kaladesh/BomatCourier.java @@ -1,146 +1,151 @@ -/* - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. - */ -package mage.sets.kaladesh; - -import java.util.UUID; -import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.DiscardHandCost; -import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.costs.mana.ColoredManaCost; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.keyword.HasteAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.constants.CardType; -import mage.constants.ColoredManaSymbol; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; -import mage.util.CardUtil; - -/** - * - * @author emerald000 - */ -public class BomatCourier extends CardImpl { - - public BomatCourier(UUID ownerId) { - super(ownerId, 199, "Bomat Courier", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}"); - this.expansionSetCode = "KLD"; - this.subtype.add("Construct"); - this.power = new MageInt(1); - this.toughness = new MageInt(1); - - // Haste - this.addAbility(HasteAbility.getInstance()); - - // Whenever Bomat Courier attacks, exile the top card of your library face down. - this.addAbility(new AttacksTriggeredAbility(new BomatCourierExileEffect(), false)); - - // {R}, Discard your hand, Sacrifice Bomat Courier: Put all cards exiled with Bomat Courier into their owners' hands. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BomatCourierReturnEffect(), new ColoredManaCost(ColoredManaSymbol.R)); - ability.addCost(new DiscardHandCost()); - ability.addCost(new SacrificeSourceCost()); - this.addAbility(ability); - } - - public BomatCourier(final BomatCourier card) { - super(card); - } - - @Override - public BomatCourier copy() { - return new BomatCourier(this); - } -} - -class BomatCourierExileEffect extends OneShotEffect { - - BomatCourierExileEffect() { - super(Outcome.Exile); - this.staticText = "exile the top card of your library face down"; - } - - BomatCourierExileEffect(final BomatCourierExileEffect effect) { - super(effect); - } - - @Override - public BomatCourierExileEffect copy() { - return new BomatCourierExileEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - UUID exileZoneId = CardUtil.getCardExileZoneId(game, source); - card.setFaceDown(true, game); - controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName()); - card.setFaceDown(true, game); - return true; - } - } - return false; - } -} - -class BomatCourierReturnEffect extends OneShotEffect { - - BomatCourierReturnEffect() { - super(Outcome.DrawCard); - this.staticText = "Put all cards exiled with {this} into their owners' hands"; - } - - BomatCourierReturnEffect(final BomatCourierReturnEffect effect) { - super(effect); - } - - @Override - public BomatCourierReturnEffect copy() { - return new BomatCourierReturnEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - return controller.moveCards(game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source.getSourceId(), true)), Zone.HAND, source, game); - } - return false; - } -} +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.kaladesh; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardHandCost; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.ExileZone; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author emerald000 + */ +public class BomatCourier extends CardImpl { + + public BomatCourier(UUID ownerId) { + super(ownerId, 199, "Bomat Courier", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}"); + this.expansionSetCode = "KLD"; + this.subtype.add("Construct"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever Bomat Courier attacks, exile the top card of your library face down. + this.addAbility(new AttacksTriggeredAbility(new BomatCourierExileEffect(), false)); + + // {R}, Discard your hand, Sacrifice Bomat Courier: Put all cards exiled with Bomat Courier into their owners' hands. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BomatCourierReturnEffect(), new ColoredManaCost(ColoredManaSymbol.R)); + ability.addCost(new DiscardHandCost()); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + public BomatCourier(final BomatCourier card) { + super(card); + } + + @Override + public BomatCourier copy() { + return new BomatCourier(this); + } +} + +class BomatCourierExileEffect extends OneShotEffect { + + BomatCourierExileEffect() { + super(Outcome.Exile); + this.staticText = "exile the top card of your library face down"; + } + + BomatCourierExileEffect(final BomatCourierExileEffect effect) { + super(effect); + } + + @Override + public BomatCourierExileEffect copy() { + return new BomatCourierExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller != null && sourceObject != null) { + Card card = controller.getLibrary().getFromTop(game); + if (card != null) { + UUID exileZoneId = CardUtil.getCardExileZoneId(game, source); + card.setFaceDown(true, game); + controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName()); + card.setFaceDown(true, game); + return true; + } + } + return false; + } +} + +class BomatCourierReturnEffect extends OneShotEffect { + + BomatCourierReturnEffect() { + super(Outcome.DrawCard); + this.staticText = "Put all cards exiled with {this} into their owners' hands"; + } + + BomatCourierReturnEffect(final BomatCourierReturnEffect effect) { + super(effect); + } + + @Override + public BomatCourierReturnEffect copy() { + return new BomatCourierReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source.getSourceId(), true)); + if (exileZone != null) { + controller.moveCards(exileZone, Zone.HAND, source, game); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/kaladesh/CapturedByTheConsulate.java b/Mage.Sets/src/mage/sets/kaladesh/CapturedByTheConsulate.java index 5d91046d91e..bb0eb4cc8d2 100644 --- a/Mage.Sets/src/mage/sets/kaladesh/CapturedByTheConsulate.java +++ b/Mage.Sets/src/mage/sets/kaladesh/CapturedByTheConsulate.java @@ -35,7 +35,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.combat.CantAttackBlockAttachedEffect; +import mage.abilities.effects.common.combat.CantAttackAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.constants.AttachmentType; @@ -81,8 +81,8 @@ public class CapturedByTheConsulate extends CardImpl { Ability ability = new EnchantAbility(auraTarget.getTargetName()); this.addAbility(ability); - // Enchanted creature can't attack or block. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackBlockAttachedEffect(AttachmentType.AURA))); + // Enchanted creature can't attack. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackAttachedEffect(AttachmentType.AURA))); // Whenever an opponent casts a spell, if it has a single target, change the target to enchanted creature if able. this.addAbility(new CapturedByTheConsulateTriggeredAbility(Zone.BATTLEFIELD, new CapturedByTheConsulateEffect())); @@ -141,7 +141,8 @@ class CapturedByTheConsulateTriggeredAbility extends TriggeredAbilityImpl { } if (stackObject != null) { int numberOfTargets = 0; - for (Mode mode : stackObject.getStackAbility().getModes().getSelectedModes()) { + for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) { + Mode mode = stackObject.getStackAbility().getModes().get(modeId); for (Target target : mode.getTargets()) { numberOfTargets += target.getTargets().size(); } diff --git a/Mage.Sets/src/mage/sets/kaladesh/CombustibleGearhulk.java b/Mage.Sets/src/mage/sets/kaladesh/CombustibleGearhulk.java index d5645ae1394..228d426b2c7 100644 --- a/Mage.Sets/src/mage/sets/kaladesh/CombustibleGearhulk.java +++ b/Mage.Sets/src/mage/sets/kaladesh/CombustibleGearhulk.java @@ -99,7 +99,7 @@ class CombustibleGearhulkEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (controller != null && sourcePermanent != null) { UUID opponentId; if (game.getOpponents(controller.getId()).size() == 1) { diff --git a/Mage.Sets/src/mage/sets/kaladesh/EraOfInnovation.java b/Mage.Sets/src/mage/sets/kaladesh/EraOfInnovation.java index 48ea1a388b2..bcc9ff3ef47 100644 --- a/Mage.Sets/src/mage/sets/kaladesh/EraOfInnovation.java +++ b/Mage.Sets/src/mage/sets/kaladesh/EraOfInnovation.java @@ -45,6 +45,7 @@ import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.SubtypePredicate; /** * @@ -52,21 +53,21 @@ import mage.filter.predicate.mageobject.CardTypePredicate; */ public class EraOfInnovation extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("an artifact or creature"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("an artifact or Artificer"); static { filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.CREATURE))); + new SubtypePredicate("Artificer"))); } public EraOfInnovation(UUID ownerId) { super(ownerId, 45, "Era of Innovation", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); this.expansionSetCode = "KLD"; - // Whenever an artifact or creature enters the battlefield under you control, you may pay {1}. If you do, you get {E}{E}. + // Whenever an artifact or Artificer enters the battlefield under you control, you may pay {1}. If you do, you get {E}{E}. Effect effect = new DoIfCostPaid(new GetEnergyCountersControllerEffect(2), new GenericManaCost(1)); this.addAbility(new EntersBattlefieldAllTriggeredAbility(effect, filter, - "Whenever an artifact or creature enters the battlefield under you control, you may pay {1}. If you do, you get {E}{E}.")); + "Whenever an artifact or Artificer enters the battlefield under you control, you may pay {1}. If you do, you get {E}{E}.")); // {E}{E}{E}{E}{E}{E}, Sacrifice Era of Innovation: Draw three cards. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(3), new PayEnergyCost(6)); diff --git a/Mage.Sets/src/mage/sets/kaladesh/InventorsGoggles.java b/Mage.Sets/src/mage/sets/kaladesh/InventorsGoggles.java index 630766e4ab7..bba7c0e19e7 100644 --- a/Mage.Sets/src/mage/sets/kaladesh/InventorsGoggles.java +++ b/Mage.Sets/src/mage/sets/kaladesh/InventorsGoggles.java @@ -1,82 +1,81 @@ -/* - * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of BetaSteward_at_googlemail.com. - */ -package mage.sets.kaladesh; - -import java.util.UUID; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.continuous.BoostEquippedEffect; -import mage.abilities.keyword.EquipAbility; -import mage.cards.CardImpl; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.SetTargetPointer; -import mage.constants.Zone; -import mage.filter.FilterPermanent; - -/** - * - * @author emerald000 - */ -public class InventorsGoggles extends CardImpl { - - public InventorsGoggles(UUID ownerId) { - super(ownerId, 218, "Inventor's Goggles", Rarity.COMMON, new CardType[]{CardType.ARTIFACT}, "{1}"); - this.expansionSetCode = "KLD"; - this.subtype.add("Equipment"); - - // Equipped creature gets +1/+2. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(1, 2, Duration.WhileOnBattlefield))); - - // Whenever an Artificer enters the battlefield under your control, you may attach Inventor's Goggles to it. - this.addAbility(new EntersBattlefieldAllTriggeredAbility( - Zone.BATTLEFIELD, - new AttachEffect(Outcome.BoostCreature, "attach {this} to it"), - new FilterPermanent("Artificer", "Artificer"), - true, - SetTargetPointer.PERMANENT, - null, - true)); - - // Equip {2} - this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(2))); - } - - public InventorsGoggles(final InventorsGoggles card) { - super(card); - } - - @Override - public InventorsGoggles copy() { - return new InventorsGoggles(this); - } -} +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.kaladesh; + +import java.util.UUID; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.FilterPermanent; + +/** + * + * @author emerald000 + */ +public class InventorsGoggles extends CardImpl { + + public InventorsGoggles(UUID ownerId) { + super(ownerId, 218, "Inventor's Goggles", Rarity.COMMON, new CardType[]{CardType.ARTIFACT}, "{1}"); + this.expansionSetCode = "KLD"; + this.subtype.add("Equipment"); + + // Equipped creature gets +1/+2. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(1, 2, Duration.WhileOnBattlefield))); + + // Whenever an Artificer enters the battlefield under your control, you may attach Inventor's Goggles to it. + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + Zone.BATTLEFIELD, + new AttachEffect(Outcome.BoostCreature, "attach {this} to it"), + new FilterPermanent("Artificer", "Artificer"), + true, + SetTargetPointer.PERMANENT, + null)); + + // Equip {2} + this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(2))); + } + + public InventorsGoggles(final InventorsGoggles card) { + super(card); + } + + @Override + public InventorsGoggles copy() { + return new InventorsGoggles(this); + } +} diff --git a/Mage.Sets/src/mage/sets/kaladesh/LostLegacy.java b/Mage.Sets/src/mage/sets/kaladesh/LostLegacy.java index eca25bd5efb..7451db1da1f 100644 --- a/Mage.Sets/src/mage/sets/kaladesh/LostLegacy.java +++ b/Mage.Sets/src/mage/sets/kaladesh/LostLegacy.java @@ -81,7 +81,7 @@ class LostLegacyEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExi public boolean apply(Game game, Ability source) { String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY); Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (targetPlayer != null) { + if (targetPlayer != null && cardName != null && !cardName.isEmpty()) { FilterCard filter = new FilterCard(); filter.add(new NamePredicate(cardName)); int cardsInHandBefore = targetPlayer.getHand().count(filter, game); diff --git a/Mage.Sets/src/mage/sets/kaladesh/MultiformWonder.java b/Mage.Sets/src/mage/sets/kaladesh/MultiformWonder.java index bb955975e05..6ec7d861bbe 100644 --- a/Mage.Sets/src/mage/sets/kaladesh/MultiformWonder.java +++ b/Mage.Sets/src/mage/sets/kaladesh/MultiformWonder.java @@ -187,10 +187,10 @@ class MultiformWonder2Effect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - Permanent target = game.getPermanent(source.getFirstTarget()); - if (target != null) { - target.addPower(power); - target.addToughness(toughness); + Permanent sourceObject = game.getPermanent(source.getSourceId()); + if (sourceObject != null) { + sourceObject.addPower(power); + sourceObject.addToughness(toughness); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/kaladesh/Panharmonicon.java b/Mage.Sets/src/mage/sets/kaladesh/Panharmonicon.java index f335d301cab..f25b93db0ab 100644 --- a/Mage.Sets/src/mage/sets/kaladesh/Panharmonicon.java +++ b/Mage.Sets/src/mage/sets/kaladesh/Panharmonicon.java @@ -41,7 +41,7 @@ import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; +import mage.game.events.NumberOfTriggersEvent; /** * @@ -85,15 +85,28 @@ class PanharmoniconEffect extends ReplacementEffectImpl { @Override public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.ENTERS_THE_BATTLEFIELD; + return event.getType() == EventType.NUMBER_OF_TRIGGERS; } @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event instanceof EntersTheBattlefieldEvent) { - Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); - if (permanent != null) { - return permanent.getCardType().contains(CardType.ARTIFACT) || permanent.getCardType().contains(CardType.CREATURE); + if (event instanceof NumberOfTriggersEvent) { + NumberOfTriggersEvent numberOfTriggersEvent = (NumberOfTriggersEvent) event; + // Only triggers of the controller of Panharmonicon + if (source.getControllerId().equals(event.getPlayerId())) { + GameEvent sourceEvent = numberOfTriggersEvent.getSourceEvent(); + // Only EtB triggers + if (sourceEvent.getType() == EventType.ENTERS_THE_BATTLEFIELD && sourceEvent instanceof EntersTheBattlefieldEvent) { + EntersTheBattlefieldEvent entersTheBattlefieldEvent = (EntersTheBattlefieldEvent) sourceEvent; + // Only for entering artifacts or creatures + if (entersTheBattlefieldEvent.getTarget().getCardType().contains(CardType.ARTIFACT) + || entersTheBattlefieldEvent.getTarget().getCardType().contains(CardType.CREATURE)) { + // Only for triggers of permanents + if (game.getPermanent(numberOfTriggersEvent.getSourceId()) != null) { + return true; + } + } + } } } return false; diff --git a/Mage.Sets/src/mage/sets/kaladesh/SequesteredStash.java b/Mage.Sets/src/mage/sets/kaladesh/SequesteredStash.java index 24d0e111b52..89080e4e5cc 100644 --- a/Mage.Sets/src/mage/sets/kaladesh/SequesteredStash.java +++ b/Mage.Sets/src/mage/sets/kaladesh/SequesteredStash.java @@ -33,15 +33,18 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.PutTopCardOfLibraryIntoGraveControllerEffect; import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterArtifactCard; +import mage.game.Game; +import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; /** @@ -61,10 +64,7 @@ public class SequesteredStash extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutTopCardOfLibraryIntoGraveControllerEffect(5), new GenericManaCost(4)); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); - Effect effect = new PutOnLibraryTargetEffect(true); - effect.setText("Then you may put an artifact card from your graveyard on top of your library"); - ability.addEffect(effect); - ability.addTarget(new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from your graveyard"))); + ability.addEffect(new SequesteredStashEffect()); this.addAbility(ability); } @@ -78,3 +78,39 @@ public class SequesteredStash extends CardImpl { return new SequesteredStash(this); } } + +class SequesteredStashEffect extends OneShotEffect { + + public SequesteredStashEffect() { + super(Outcome.Benefit); + this.staticText = "Then you may put an artifact card from your graveyard on top of your library"; + } + + public SequesteredStashEffect(final SequesteredStashEffect effect) { + super(effect); + } + + @Override + public SequesteredStashEffect copy() { + return new SequesteredStashEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from your graveyard")); + target.setNotTarget(true); + if (target.canChoose(source.getSourceId(), source.getControllerId(), game) + && controller.chooseUse(outcome, "Put an artifact card from your graveyard to library?", source, game) + && controller.choose(outcome, target, source.getSourceId(), game)) { + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + controller.moveCards(card, Zone.LIBRARY, source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/kaladesh/SparkOfCreativity.java b/Mage.Sets/src/mage/sets/kaladesh/SparkOfCreativity.java index 6f64ad02781..b0d562e2ca2 100644 --- a/Mage.Sets/src/mage/sets/kaladesh/SparkOfCreativity.java +++ b/Mage.Sets/src/mage/sets/kaladesh/SparkOfCreativity.java @@ -44,6 +44,7 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.target.common.TargetCreaturePermanent; /** * @@ -57,6 +58,7 @@ public class SparkOfCreativity extends CardImpl { // Choose target creature. Exile the top card of your library. You may have Spark of Creativity deal damage to that creature equal to the converted mana cost of the exiled card. If you don't, you may play that card until end of turn. this.getSpellAbility().addEffect(new SparkOfCreativityEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } public SparkOfCreativity(final SparkOfCreativity card) { @@ -142,9 +144,7 @@ class SparkOfCreativityPlayEffect extends AsThoughEffectImpl { if (objectReference.refersTo(objectId, game) && affectedControllerId.equals(source.getControllerId())) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - if (controller.chooseUse(outcome, "Play the exiled card?", source, game)) { - return true; - } + return true; } else { discard(); } diff --git a/Mage.Sets/src/mage/sets/kaladesh/SubtleStrike.java b/Mage.Sets/src/mage/sets/kaladesh/SubtleStrike.java index 746681c62e9..5cd57faff4a 100644 --- a/Mage.Sets/src/mage/sets/kaladesh/SubtleStrike.java +++ b/Mage.Sets/src/mage/sets/kaladesh/SubtleStrike.java @@ -52,11 +52,15 @@ public class SubtleStrike extends CardImpl { this.getSpellAbility().getModes().setMinModes(1); this.getSpellAbility().getModes().setMaxModes(2); // • Target creature gets -1/-1 until end of turn. - this.getSpellAbility().addEffect(new BoostTargetEffect(-1, -1, Duration.EndOfTurn)); + BoostTargetEffect minusOneMinusOne = new BoostTargetEffect(-1, -1, Duration.EndOfTurn); + minusOneMinusOne.setText("Target creature gets -1/-1 until end of turn"); + this.getSpellAbility().addEffect(minusOneMinusOne); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); // • Put a +1/+1 counter on target creature. Mode mode1 = new Mode(); - mode1.getEffects().add(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + AddCountersTargetEffect plusOnePlusOneCounter = new AddCountersTargetEffect(CounterType.P1P1.createInstance()); + plusOnePlusOneCounter.setText("Put a +1/+1 counter on target creature"); + mode1.getEffects().add(plusOnePlusOneCounter); mode1.getTargets().add(new TargetCreaturePermanent()); this.getSpellAbility().addMode(mode1); diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/AltarOfTheBrood.java b/Mage.Sets/src/mage/sets/khansoftarkir/AltarOfTheBrood.java index 781ad817f93..6aa6bb85c89 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/AltarOfTheBrood.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/AltarOfTheBrood.java @@ -56,7 +56,7 @@ public class AltarOfTheBrood extends CardImpl { // Whenever another permanent enters the battlefield under your control, each opponent puts the top card of his or her library into his or her graveyard. this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, - new PutTopCardOfLibraryIntoGraveEachPlayerEffect(1, TargetController.OPPONENT), filter, false)); + new PutTopCardOfLibraryIntoGraveEachPlayerEffect(1, TargetController.OPPONENT), filter, false, null, true)); } public AltarOfTheBrood(final AltarOfTheBrood card) { diff --git a/Mage.Sets/src/mage/sets/legions/BroodSliver.java b/Mage.Sets/src/mage/sets/legions/BroodSliver.java index 86ea9e7ec7b..0367dcdcba8 100644 --- a/Mage.Sets/src/mage/sets/legions/BroodSliver.java +++ b/Mage.Sets/src/mage/sets/legions/BroodSliver.java @@ -29,15 +29,18 @@ package mage.sets.legions; import java.util.UUID; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.CreateTokenTargetEffect; +import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.SetTargetPointer; import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; import mage.game.permanent.token.SliverToken; +import mage.players.Player; /** * @@ -54,10 +57,8 @@ public class BroodSliver extends CardImpl { this.toughness = new MageInt(3); // Whenever a Sliver deals combat damage to a player, its controller may put a 1/1 colorless Sliver creature token onto the battlefield. - Effect effect = new CreateTokenTargetEffect(new SliverToken()); - effect.setText("its controller may put a 1/1 colorless Sliver creature token onto the battlefield"); - this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility(effect, - new FilterCreaturePermanent("Sliver", "a Sliver"), true, SetTargetPointer.PLAYER, true)); + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility(new BroodSliverEffect(), + new FilterCreaturePermanent("Sliver", "a Sliver"), false, SetTargetPointer.PLAYER, true)); } public BroodSliver(final BroodSliver card) { @@ -69,3 +70,32 @@ public class BroodSliver extends CardImpl { return new BroodSliver(this); } } + +class BroodSliverEffect extends OneShotEffect { + + public BroodSliverEffect() { + super(Outcome.PutCardInPlay); + this.staticText = "its controller may put a 1/1 colorless Sliver creature token onto the battlefield"; + } + + public BroodSliverEffect(final BroodSliverEffect effect) { + super(effect); + } + + @Override + public BroodSliverEffect copy() { + return new BroodSliverEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player permanentController = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (permanentController != null) { + if (permanentController.chooseUse(outcome, "put a 1/1 colorless Sliver creature token onto the battlefield", source, game)) { + return new SliverToken().putOntoBattlefield(1, game, source.getSourceId(), permanentController.getId()); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/lorwyn/SilvergillDouser.java b/Mage.Sets/src/mage/sets/lorwyn/SilvergillDouser.java new file mode 100644 index 00000000000..31f291c3e22 --- /dev/null +++ b/Mage.Sets/src/mage/sets/lorwyn/SilvergillDouser.java @@ -0,0 +1,84 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.lorwyn; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Styxo + */ +public class SilvergillDouser extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Merfolk and/or Faeries you control"); + + static { + filter.add(Predicates.or(new SubtypePredicate("Merfolk"), new SubtypePredicate("Faerie"))); + } + + public SilvergillDouser(UUID ownerId) { + super(ownerId, 87, "Silvergill Douser", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{U}"); + this.expansionSetCode = "LRW"; + this.subtype.add("Merfolk"); + this.subtype.add("Wizard"); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {tap}: Target creature gets -X/-0 until end of turn, where X is the number of Merfolk and/or Faeries you control. + DynamicValue number = new PermanentsOnBattlefieldCount(new FilterControlledCreaturePermanent(filter), -1); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(number, new StaticValue(0), Duration.EndOfTurn, true), new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public SilvergillDouser(final SilvergillDouser card) { + super(card); + } + + @Override + public SilvergillDouser copy() { + return new SilvergillDouser(this); + } +} diff --git a/Mage.Sets/src/mage/sets/magic2014/StrionicResonator.java b/Mage.Sets/src/mage/sets/magic2014/StrionicResonator.java index 03f256ba0d9..1f56faed819 100644 --- a/Mage.Sets/src/mage/sets/magic2014/StrionicResonator.java +++ b/Mage.Sets/src/mage/sets/magic2014/StrionicResonator.java @@ -27,12 +27,9 @@ */ package mage.sets.magic2014; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.TriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; @@ -42,14 +39,11 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.Filter; -import mage.filter.FilterAbility; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; -import mage.game.stack.StackObject; import mage.players.Player; -import mage.target.TargetObject; +import mage.target.common.TargetTriggeredAbility; /** * @@ -96,7 +90,7 @@ class StrionicResonatorEffect extends OneShotEffect { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (controller != null && sourcePermanent != null) { stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); - game.informPlayers(new StringBuilder(sourcePermanent.getName()).append(": ").append(controller.getLogName()).append(" copied activated ability").toString()); + game.informPlayers(new StringBuilder(sourcePermanent.getName()).append(": ").append(controller.getLogName()).append(" copied triggered ability").toString()); return true; } } @@ -116,76 +110,3 @@ class StrionicResonatorEffect extends OneShotEffect { return sb.toString(); } } - -class TargetTriggeredAbility extends TargetObject { - - public TargetTriggeredAbility() { - this.minNumberOfTargets = 1; - this.maxNumberOfTargets = 1; - this.zone = Zone.STACK; - this.targetName = "target triggered ability you control"; - } - - public TargetTriggeredAbility(final TargetTriggeredAbility target) { - super(target); - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - if (source != null && source.getSourceId().equals(id)) { - return false; - } - - StackObject stackObject = game.getStack().getStackObject(id); - return stackObject.getStackAbility() != null - && (stackObject.getStackAbility() instanceof TriggeredAbility) - && source != null - && stackObject.getStackAbility().getControllerId().equals(source.getControllerId()); - } - - @Override - public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { - return canChoose(sourceControllerId, game); - } - - @Override - public boolean canChoose(UUID sourceControllerId, Game game) { - for (StackObject stackObject : game.getStack()) { - if (stackObject.getStackAbility() != null - && stackObject.getStackAbility() instanceof TriggeredAbility - && stackObject.getStackAbility().getControllerId().equals(sourceControllerId)) { - return true; - } - } - return false; - } - - @Override - public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - return possibleTargets(sourceControllerId, game); - } - - @Override - public Set possibleTargets(UUID sourceControllerId, Game game) { - Set possibleTargets = new HashSet<>(); - for (StackObject stackObject : game.getStack()) { - if (stackObject.getStackAbility() != null - && stackObject.getStackAbility() instanceof TriggeredAbility - && stackObject.getStackAbility().getControllerId().equals(sourceControllerId)) { - possibleTargets.add(stackObject.getStackAbility().getId()); - } - } - return possibleTargets; - } - - @Override - public TargetTriggeredAbility copy() { - return new TargetTriggeredAbility(this); - } - - @Override - public Filter getFilter() { - return new FilterAbility(); - } - -} diff --git a/Mage.Sets/src/mage/sets/magicorigins/PsychicRebuttal.java b/Mage.Sets/src/mage/sets/magicorigins/PsychicRebuttal.java index 9aa89e0b2f2..533925a5786 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/PsychicRebuttal.java +++ b/Mage.Sets/src/mage/sets/magicorigins/PsychicRebuttal.java @@ -135,7 +135,8 @@ class PsychicRebuttalPredicate implements ObjectPlayerPredicate("{2}{W}")); ability.addCost(new RemoveCountersSourceCost(CounterType.P1P1.createInstance())); @@ -81,42 +83,3 @@ public class TwilightDrover extends CardImpl { return new TwilightDrover(this); } } - -class TwilightDroverTriggeredAbility extends TriggeredAbilityImpl { - - TwilightDroverTriggeredAbility() { - super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false); - } - - TwilightDroverTriggeredAbility(final TwilightDroverTriggeredAbility ability) { - super(ability); - } - - @Override - public TwilightDroverTriggeredAbility copy() { - return new TwilightDroverTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() == Zone.BATTLEFIELD) { - UUID targetId = event.getTargetId(); - Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); - if (permanent != null) { - return permanent.getCardType().contains(CardType.CREATURE) && permanent instanceof PermanentToken; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature token leaves the battlefield, put a +1/+1 counter on {this}"; - } -} diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/LightmineField.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/LightmineField.java index 62111bf7669..0e38b02badf 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/LightmineField.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/LightmineField.java @@ -52,7 +52,6 @@ public class LightmineField extends CardImpl { super(ownerId, 32, "Lightmine Field", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); this.expansionSetCode = "ROE"; - // Whenever one or more creatures attack, Lightmine Field deals damage to each of those creatures equal to the number of attacking creatures. this.addAbility(new LightmineFieldTriggeredAbility()); } @@ -122,7 +121,7 @@ class LightmineFieldEffect extends OneShotEffect { for (UUID attacker : attackers) { Permanent creature = game.getPermanent(attacker); if (creature != null) { - creature.damage(damage, source.getSourceId(), game, false, false); + creature.damage(damage, source.getSourceId(), game, false, true); } } return true; diff --git a/Mage.Sets/src/mage/sets/scourge/GripOfChaos.java b/Mage.Sets/src/mage/sets/scourge/GripOfChaos.java index 4f7131948dc..53d94e4ccba 100644 --- a/Mage.Sets/src/mage/sets/scourge/GripOfChaos.java +++ b/Mage.Sets/src/mage/sets/scourge/GripOfChaos.java @@ -28,7 +28,6 @@ package mage.sets.scourge; import java.util.Iterator; -import java.util.Random; import java.util.Set; import java.util.UUID; import mage.abilities.Ability; @@ -108,7 +107,8 @@ class GripOfChaosTriggeredAbility extends TriggeredAbilityImpl { } if (stackObject != null) { int numberOfTargets = 0; - for (Mode mode : stackObject.getStackAbility().getModes().getSelectedModes()) { + for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) { + Mode mode = stackObject.getStackAbility().getModes().get(modeId); for (Target target : mode.getTargets()) { numberOfTargets += target.getTargets().size(); } @@ -149,7 +149,8 @@ class GripOfChaosEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { StackObject stackObject = game.getStack().getStackObject(this.getTargetPointer().getFirst(game, source)); if (stackObject != null) { - for (Mode mode : stackObject.getStackAbility().getModes().getSelectedModes()) { + for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) { + Mode mode = stackObject.getStackAbility().getModes().get(modeId); for (Target target : mode.getTargets()) { UUID oldTargetId = target.getFirstTarget(); Set possibleTargets = target.possibleTargets(source.getSourceId(), source.getControllerId(), game); diff --git a/Mage.Sets/src/mage/sets/scourge/KrosanDrover.java b/Mage.Sets/src/mage/sets/scourge/KrosanDrover.java new file mode 100644 index 00000000000..35c049026b3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/KrosanDrover.java @@ -0,0 +1,75 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.scourge; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.Filter.ComparisonType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; + +/** + * + * @author Eirkei + */ +public class KrosanDrover extends CardImpl { + + private static final FilterCard filter = new FilterCard("Creature spells with converted mana cost 6 or greater"); + + static { + filter.add(new CardTypePredicate(CardType.CREATURE)); + filter.add(new ConvertedManaCostPredicate(ComparisonType.GreaterThan, 5)); + } + + public KrosanDrover(UUID ownerId) { + super(ownerId, 122, "Krosan Drover", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{3}{G}"); + this.expansionSetCode = "SCG"; + this.subtype.add("Elf"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Creature spells you cast with converted mana cost 6 or greater cost {2} less to cast. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 2))); + } + + public KrosanDrover(final KrosanDrover card) { + super(card); + } + + @Override + public KrosanDrover copy() { + return new KrosanDrover(this); + } +} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/Tatterkite.java b/Mage.Sets/src/mage/sets/shadowmoor/Tatterkite.java index f10c0e25d3c..d17dedb4b1a 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/Tatterkite.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/Tatterkite.java @@ -29,19 +29,14 @@ package mage.sets.shadowmoor; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.ruleModifying.CantHaveCountersSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; /** * @@ -60,7 +55,7 @@ public class Tatterkite extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Tatterkite can't have counters placed on it. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new CantHaveCountersSourceEffect(Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantHaveCountersSourceEffect())); } @@ -73,34 +68,3 @@ public class Tatterkite extends CardImpl { return new Tatterkite(this); } } - -class CantHaveCountersSourceEffect extends ContinuousRuleModifyingEffectImpl { - - public CantHaveCountersSourceEffect(Duration duration) { - super(duration, Outcome.Detriment); - staticText = "{this} can't have counters placed on it"; - } - - public CantHaveCountersSourceEffect(final CantHaveCountersSourceEffect effect) { - super(effect); - } - - @Override - public CantHaveCountersSourceEffect copy() { - return new CantHaveCountersSourceEffect(this); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.ADD_COUNTERS; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - UUID sourceId = source.getSourceId(); - if (sourceId != null) { - return sourceId.equals(event.getTargetId()); - } - return false; - } -} diff --git a/Mage.Sets/src/mage/sets/shadowsoverinnistrad/AccursedWitch.java b/Mage.Sets/src/mage/sets/shadowsoverinnistrad/AccursedWitch.java index bc42294c7f1..6e0f6c19ae0 100644 --- a/Mage.Sets/src/mage/sets/shadowsoverinnistrad/AccursedWitch.java +++ b/Mage.Sets/src/mage/sets/shadowsoverinnistrad/AccursedWitch.java @@ -140,7 +140,8 @@ class AccursedWitchSpellsCostReductionEffect extends CostModificationEffectImpl public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify instanceof SpellAbility) { if (game.getOpponents(source.getControllerId()).contains(abilityToModify.getControllerId())) { - for (Mode mode : abilityToModify.getModes().getSelectedModes()) { + for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { + Mode mode = abilityToModify.getModes().get(modeId); for (Target target : mode.getTargets()) { for (UUID targetUUID : target.getTargets()) { Permanent permanent = game.getPermanent(targetUUID); diff --git a/Mage.Sets/src/mage/sets/shadowsoverinnistrad/InfectiousCurse.java b/Mage.Sets/src/mage/sets/shadowsoverinnistrad/InfectiousCurse.java index 2cc858e4bb6..03e76ef9b6e 100644 --- a/Mage.Sets/src/mage/sets/shadowsoverinnistrad/InfectiousCurse.java +++ b/Mage.Sets/src/mage/sets/shadowsoverinnistrad/InfectiousCurse.java @@ -155,7 +155,8 @@ class InfectiousCurseCostReductionEffect extends CostModificationEffectImpl { public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify instanceof SpellAbility) { if (source.getControllerId().equals(abilityToModify.getControllerId())) { - for (Mode mode : abilityToModify.getModes().getSelectedModes()) { + for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { + Mode mode = abilityToModify.getModes().get(modeId); for (Target target : mode.getTargets()) { for (UUID targetUUID : target.getTargets()) { Permanent enchantment = game.getPermanent(source.getSourceId()); diff --git a/Mage.Sets/src/mage/sets/shardsofalara/HinderingLight.java b/Mage.Sets/src/mage/sets/shardsofalara/HinderingLight.java index 7726f3ab806..6cfe4482004 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/HinderingLight.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/HinderingLight.java @@ -84,7 +84,8 @@ class HinderingLightPredicate implements ObjectPlayerPredicate liste = game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), playerId, game); + if(!liste.isEmpty()){ + player.choose(Outcome.ReturnToHand, target, source.getSourceId(), game); + + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + permanent.moveToZone(Zone.HAND, source.getSourceId(), game, false); + } + } + } + } + return true; + } + + + @Override + public Effect copy() { + return new CurfewEffect(); + } +} + diff --git a/Mage.Sets/src/mage/sets/urzassaga/Smokestack.java b/Mage.Sets/src/mage/sets/urzassaga/Smokestack.java index 42d7bcbbdb3..02fdf63ab36 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Smokestack.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Smokestack.java @@ -96,7 +96,7 @@ class SmokestackEffect extends OneShotEffect { int count = sourcePermanent.getCounters(game).getCount("Soot"); if (count > 0) { int amount = Math.min(count, game.getBattlefield().countAll(new FilterControlledPermanent(), activePlayer.getId(), game)); - Target target = new TargetControlledPermanent(amount, amount, new FilterControlledPermanent(), false); + Target target = new TargetControlledPermanent(amount, amount, new FilterControlledPermanent(), true); //A spell or ability could have removed the only legal target this player //had, if thats the case this ability should fizzle. if (target.canChoose(activePlayer.getId(), game)) { diff --git a/Mage.Sets/src/mage/sets/venservskoth/Torchling.java b/Mage.Sets/src/mage/sets/venservskoth/Torchling.java index 9b5575afa92..2d462c954d0 100644 --- a/Mage.Sets/src/mage/sets/venservskoth/Torchling.java +++ b/Mage.Sets/src/mage/sets/venservskoth/Torchling.java @@ -113,13 +113,13 @@ class TorchlingTargetPredicate implements Predicate { if (spell != null) { int numberOfTargets = 0; for (SpellAbility spellAbility : spell.getSpellAbilities()) { - for (Mode mode : spellAbility.getModes().getSelectedModes()) { + for (UUID modeId : spellAbility.getModes().getSelectedModes()) { + Mode mode = spellAbility.getModes().get(modeId); for (Target target : mode.getTargets()) { for (UUID targetId : target.getTargets()) { if (!targetId.equals(sourceId)) { return false; - } - else { + } else { numberOfTargets++; } } diff --git a/Mage.Sets/src/mage/sets/vintagemasters/KaerveksTorch.java b/Mage.Sets/src/mage/sets/vintagemasters/KaerveksTorch.java index 2af317e2e49..14b5866ca5f 100644 --- a/Mage.Sets/src/mage/sets/vintagemasters/KaerveksTorch.java +++ b/Mage.Sets/src/mage/sets/vintagemasters/KaerveksTorch.java @@ -96,7 +96,8 @@ class KaerveksTorchCostIncreaseEffect extends CostModificationEffectImpl { @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { if (abilityToModify instanceof SpellAbility || abilityToModify instanceof FlashbackAbility) { - for (Mode mode : abilityToModify.getModes().getSelectedModes()) { + for (UUID modeId : abilityToModify.getModes().getSelectedModes()) { + Mode mode = abilityToModify.getModes().get(modeId); for (Target target : mode.getTargets()) { for (UUID targetId : target.getTargets()) { if (targetId.equals(source.getSourceObject(game).getId())) { diff --git a/Mage.Sets/src/mage/sets/worldwake/Terastodon.java b/Mage.Sets/src/mage/sets/worldwake/Terastodon.java index ad2eb45ae7c..30ff2d09ecb 100644 --- a/Mage.Sets/src/mage/sets/worldwake/Terastodon.java +++ b/Mage.Sets/src/mage/sets/worldwake/Terastodon.java @@ -111,7 +111,7 @@ class TerastodonEffect extends OneShotEffect { if (destroyedPermanents.containsKey(permanent.getControllerId())) { numberPermanents = destroyedPermanents.get(permanent.getControllerId()); } - destroyedPermanents.put(permanent.getControllerId(), numberPermanents); + destroyedPermanents.put(permanent.getControllerId(), numberPermanents + 1); } } } diff --git a/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastDestroySpellsTest.java b/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastDestroySpellsTest.java index 57f34cbc672..792284f65ab 100644 --- a/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastDestroySpellsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastDestroySpellsTest.java @@ -38,10 +38,6 @@ import org.mage.test.serverside.base.CardTestPlayerBaseAI; */ public class CastDestroySpellsTest extends CardTestPlayerBaseAI { - /** - * - * - */ @Test public void testOrzhovCharm() { // Choose one - @@ -58,6 +54,8 @@ public class CastDestroySpellsTest extends CardTestPlayerBaseAI { // Cycling abilities you activate cost you up to {2} less to activate. addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Orzhov Charm", "Silvercoat Lion"); + setModeChoice(playerA, "2"); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java index 5a2fa4d5402..e591cae4831 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/UndyingTest.java @@ -219,7 +219,7 @@ public class UndyingTest extends CardTestPlayerBase { } /** - * Tatterkite is getting counters on it, i have him in a edh deck with + * Tatterkite is getting counters on it, I have him in a edh deck with * Mikaeus, the Lunarch and when Tatterkite dies it triggers the undying and * he gets the +1/+1 counters */ diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrBothTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrBothTest.java new file mode 100644 index 00000000000..52aef300aaf --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/modal/OneOrBothTest.java @@ -0,0 +1,107 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.modal; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class OneOrBothTest extends CardTestPlayerBase { + + @Test + public void testSubtleStrikeFirstMode() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Choose one or both — + // • Target creature gets -1/-1 until end of turn. + // • Put a +1/+1 counter on target creature. + addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B} + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox"); + setModeChoice(playerA, "1"); + setModeChoice(playerA, null); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); + assertPowerToughness(playerB, "Pillarfield Ox", 1, 3); + } + + @Test + public void testSubtleStrikeSecondMode() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Choose one or both — + // • Target creature gets -1/-1 until end of turn. + // • Put a +1/+1 counter on target creature. + addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B} + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, null); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Silvercoat Lion", 2, 2); + assertPowerToughness(playerB, "Pillarfield Ox", 3, 5); + } + + @Test + public void testSubtleStrikeBothModes() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Choose one or both — + // • Target creature gets -1/-1 until end of turn. + // • Put a +1/+1 counter on target creature. + addCard(Zone.HAND, playerA, "Subtle Strike"); // Instant {1}{B} + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Subtle Strike", "Pillarfield Ox"); + addTarget(playerA, "Silvercoat Lion"); + setModeChoice(playerA, "1"); + setModeChoice(playerA, "2"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Silvercoat Lion", 3, 3); + assertPowerToughness(playerB, "Pillarfield Ox", 1, 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/PanharmoniconTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/PanharmoniconTest.java new file mode 100644 index 00000000000..b22682e1f01 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/PanharmoniconTest.java @@ -0,0 +1,129 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.replacement; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class PanharmoniconTest extends CardTestPlayerBase { + + /** + * Check that Panharmonicon adds EtB triggers correctly. + * + */ + @Test + public void testAddsTrigger() { + // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. + addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + // Whenever another creature enters the battlefield, you gain 1 life. + addCard(Zone.HAND, playerA, "Soul Warden"); + // When Devout Monk enters the battlefield, you gain 1 life. + addCard(Zone.HAND, playerA, "Devout Monk"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Warden"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Devout Monk"); // Life: 20 + 2*1 + 2*1 = 24 + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 24); + } + + /** + * Check that Panharmonicon doesn't add to opponents' triggers. + * + */ + @Test + public void testDoesntAddOpponentsTriggers() { + // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. + addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon"); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + // Whenever another creature enters the battlefield, you gain 1 life. + addCard(Zone.HAND, playerB, "Soul Warden"); + // When Devout Monk enters the battlefield, you gain 1 life. + addCard(Zone.HAND, playerB, "Devout Monk"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Soul Warden"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Devout Monk"); // Life: 20 + 1 + 1 = 22 + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 22); + } + + /** + * Check that Panharmonicon doesn't add to lands triggers. + * + */ + @Test + public void testDoesntAddLandsTriggers() { + // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. + addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon"); + // When Radiant Fountain enters the battlefield, you gain 2 life. + addCard(Zone.HAND, playerA, "Radiant Fountain"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Radiant Fountain"); // Life: 20 + 2 = 22 + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 22); + } + + /** + * Check that Panharmonicon doesn't add to non-permanents triggers. + * + */ + @Test + public void testDoesntAddNonPermanentsTriggers() { + // If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. + addCard(Zone.BATTLEFIELD, playerA, "Panharmonicon"); + // When a Dragon enters the battlefield, you may return Bladewing's Thrall from your graveyard to the battlefield. + addCard(Zone.GRAVEYARD, playerA, "Bladewing's Thrall"); + // A 4/4 vanilla dragon + addCard(Zone.HAND, playerA, "Scion of Ugin"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scion of Ugin"); + setChoice(playerA, "No"); // Return Bladewing's Thrall from your graveyard to the battlefield? + setChoice(playerA, "Yes"); // Should not get run since there is only one trigger. + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Bladewing's Thrall", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/bfz/BrutalExpulsionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/bfz/BrutalExpulsionTest.java index 195666c6097..727efe0738b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/bfz/BrutalExpulsionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/bfz/BrutalExpulsionTest.java @@ -39,20 +39,22 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class BrutalExpulsionTest extends CardTestPlayerBase { /** - * Brutal Expulsion targeting Gideon, Ally of Zendikar. Gideon has 3 loyalty. Brutal Expulsion resolves, - * leaves 1 loyalty. I attack Gideon for 1 with a Scion token, Gideon dies. Instead of going to graveyard, - * Expulsion sends Gideon to exile. However, in game Gideon went to graveyard. + * Brutal Expulsion targeting Gideon, Ally of Zendikar. Gideon has 3 + * loyalty. Brutal Expulsion resolves, leaves 1 loyalty. I attack Gideon for + * 1 with a Scion token, Gideon dies. Instead of going to graveyard, + * Expulsion sends Gideon to exile. However, in game Gideon went to + * graveyard. */ @Test public void testPlaneswalkerExile() { // Choose one or both // - Return target spell or creature to its owner's hand; // or Brutal Expulsion deals 2 damage to target creature or planeswalker. If that permanent would be put into a graveyard this turn, exile it instead. - addCard(Zone.HAND, playerA, "Brutal Expulsion"); + addCard(Zone.HAND, playerA, "Brutal Expulsion"); // {2}{U}{R} // Shock deals 2 damage to target creature or player. - addCard(Zone.HAND, playerA, "Shock"); - addCard(Zone.BATTLEFIELD, playerA, "Island", 4); - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.HAND, playerA, "Shock"); // {R} + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); // Planeswalker with 4 loyalty. addCard(Zone.BATTLEFIELD, playerB, "Gideon, Ally of Zendikar"); @@ -60,12 +62,12 @@ public class BrutalExpulsionTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Brutal Expulsion", playerB); setModeChoice(playerA, "2"); setModeChoice(playerA, null); - setChoice(playerA, "Yes"); + setChoice(playerA, "Yes"); // Redirect to planeswalker - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shock", playerB); - setChoice(playerA, "Yes"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Shock", playerB); + setChoice(playerA, "Yes"); // Redirect to planeswalker - setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + setStopAt(1, PhaseStep.END_COMBAT); execute(); assertPermanentCount(playerB, "Gideon, Ally of Zendikar", 0); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BroodSliverTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BroodSliverTest.java index 99b54a2f340..92fb807605b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BroodSliverTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BroodSliverTest.java @@ -15,24 +15,23 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public class BroodSliverTest extends CardTestPlayerBase { - + /* Reported bug: It lets the controller of Brood Sliver choose whether or not the token is created, instead of the attacking Sliver's controller. - */ + */ @Test public void testTokenCreatedBySliverController() { - + // Brood Sliver {4}{G} 3/3 Sliver // Whenever a Sliver deals combat damage to a player, its controller may put a 1/1 colorless Sliver creature token onto the battlefield. addCard(Zone.BATTLEFIELD, playerB, "Brood Sliver"); addCard(Zone.BATTLEFIELD, playerA, "Venom Sliver"); // 1/1 deathtouch granting sliver - + attack(1, playerA, "Venom Sliver"); setChoice(playerA, "Yes"); // controller of Venom Sliver dealing damage should get the choice to create token - setChoice(playerB, "No"); // Brood Sliver controller should not be given choice in the first place setStopAt(1, PhaseStep.END_COMBAT); execute(); - + assertLife(playerB, 19); assertPermanentCount(playerA, "Sliver", 1); assertPermanentCount(playerB, "Sliver", 0); 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 fd3b5ba7906..a8856084e0b 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 @@ -287,7 +287,8 @@ public class TestPlayer implements Player { } UUID modeId = ability.getModes().getModeId(modeNr); - for (Mode mode : ability.getModes().getSelectedModes()) { + for (UUID currentModeId : ability.getModes().getSelectedModes()) { + Mode mode = ability.getModes().get(currentModeId); if (mode.getId().equals(modeId)) { selectedMode = mode; ability.getModes().setActiveMode(mode); diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 65f1095d378..c79e37f28bc 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -306,7 +306,8 @@ public abstract class AbilityImpl implements Ability { && game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL_LATE, getId(), getSourceId(), getControllerId()), this)) { return false; } - for (Mode mode : this.getModes().getSelectedModes()) { + for (UUID modeId : this.getModes().getSelectedModes()) { + Mode mode = this.getModes().get(modeId); this.getModes().setActiveMode(mode); //20121001 - 601.2c // 601.2c The player announces his or her choice of an appropriate player, object, or zone for @@ -1060,7 +1061,8 @@ public abstract class AbilityImpl implements Ability { } } else if (object instanceof Spell && ((Spell) object).getSpellAbility().getModes().size() > 1) { Modes spellModes = ((Spell) object).getSpellAbility().getModes(); - for (Mode selectedMode : spellModes.getSelectedModes()) { + for (UUID selectedModeId : spellModes.getSelectedModes()) { + Mode selectedMode = spellModes.get(selectedModeId); int item = 0; for (Mode mode : spellModes.values()) { item++; diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index 08a317f95cf..6f1ea6502a3 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -41,7 +41,6 @@ import mage.constants.TargetController; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetOpponent; -import mage.util.CardUtil; /** * @@ -49,8 +48,8 @@ import mage.util.CardUtil; */ public class Modes extends LinkedHashMap { - private Mode mode; // the current mode of the selected modes - private final ArrayList selectedModes = new ArrayList<>(); + private Mode currentMode; // the current mode of the selected modes + private final ArrayList selectedModes = new ArrayList<>(); private int minModes; private int maxModes; private TargetController modeChooser; @@ -58,11 +57,11 @@ public class Modes extends LinkedHashMap { private boolean eachModeOnlyOnce; // state if each mode can be chosen only once as long as the source object exists public Modes() { - this.mode = new Mode(); - this.put(mode.getId(), mode); + this.currentMode = new Mode(); + this.put(currentMode.getId(), currentMode); this.minModes = 1; this.maxModes = 1; - this.selectedModes.add(mode); + this.selectedModes.add(currentMode.getId()); this.modeChooser = TargetController.YOU; this.eachModeOnlyOnce = false; this.eachModeMoreThanOnce = false; @@ -75,23 +74,8 @@ public class Modes extends LinkedHashMap { this.minModes = modes.minModes; this.maxModes = modes.maxModes; - if (modes.size() == 1) { - this.mode = values().iterator().next(); - this.selectedModes.add(mode); - } else { - // probably there is still a problem with copying modes with the same mode selected multiple times. - for (Mode selectedMode : modes.getSelectedModes()) { - Mode copiedMode = selectedMode.copy(); - this.selectedModes.add(copiedMode); - if (modes.getSelectedModes().size() == 1) { - this.mode = copiedMode; - } else { - if (selectedMode.equals(modes.getMode())) { - this.mode = copiedMode; - } - } - } - } + this.currentMode = values().iterator().next(); + selectedModes.addAll(modes.getSelectedModes()); this.modeChooser = modes.modeChooser; this.eachModeOnlyOnce = modes.eachModeOnlyOnce; this.eachModeMoreThanOnce = modes.eachModeMoreThanOnce; @@ -102,21 +86,21 @@ public class Modes extends LinkedHashMap { } public Mode getMode() { - return mode; + return currentMode; } public UUID getModeId(int index) { int idx = 0; - for (Mode currentMode : this.values()) { + for (Mode mode : this.values()) { idx++; if (idx == index) { - return currentMode.getId(); + return mode.getId(); } } return null; } - public ArrayList getSelectedModes() { + public ArrayList getSelectedModes() { return selectedModes; } @@ -145,8 +129,8 @@ public class Modes extends LinkedHashMap { } public void setActiveMode(Mode mode) { - if (selectedModes.contains(mode)) { - this.mode = mode; + if (selectedModes.contains(mode.getId())) { + this.currentMode = mode; } } @@ -175,7 +159,7 @@ public class Modes extends LinkedHashMap { for (Mode mode : this.values()) { if ((!isEachModeOnlyOnce() || onceSelectedModes == null || !onceSelectedModes.contains(mode.getId())) && mode.getTargets().canChoose(source.getSourceId(), source.getControllerId(), game)) { - this.selectedModes.add(mode.copy()); + this.selectedModes.add(mode.getId()); } } if (isEachModeOnlyOnce()) { @@ -203,7 +187,7 @@ public class Modes extends LinkedHashMap { Player player = game.getPlayer(playerId); // player chooses modes manually - this.mode = null; + this.currentMode = null; while (this.selectedModes.size() < this.getMaxModes()) { Mode choice = player.chooseMode(this, source, game); if (choice == null) { @@ -212,9 +196,9 @@ public class Modes extends LinkedHashMap { } return this.selectedModes.size() >= this.getMinModes(); } - this.selectedModes.add(choice.copy()); - if (mode == null) { - mode = choice; + this.selectedModes.add(choice.getId()); + if (currentMode == null) { + currentMode = choice; } } if (isEachModeOnlyOnce()) { @@ -222,10 +206,10 @@ public class Modes extends LinkedHashMap { } return true; } - if (mode == null) { + if (currentMode == null) { this.selectedModes.clear(); Mode copiedMode = this.values().iterator().next().copy(); - this.selectedModes.add(copiedMode); + this.selectedModes.add(copiedMode.getId()); this.setActiveMode(copiedMode); } if (isEachModeOnlyOnce()) { @@ -234,27 +218,46 @@ public class Modes extends LinkedHashMap { return true; } - private void setAlreadySelectedModes(ArrayList selectedModes, Ability source, Game game) { - String key = getKey(source, game); - Set onceSelectedModes = (Set) game.getState().getValue(key); - if (onceSelectedModes == null) { - onceSelectedModes = new HashSet<>(); + /** + * Saves the already selected modes to the state value + * + * @param selectedModes + * @param source + * @param game + */ + private void setAlreadySelectedModes(ArrayList selectedModes, Ability source, Game game) { + for (UUID modeId : selectedModes) { + String key = getKey(source, game, modeId); + game.getState().setValue(key, true); } - for (Mode mode : selectedModes) { - onceSelectedModes.add(mode.getId()); - } - - game.getState().setValue(key, onceSelectedModes); } + // The already once selected modes for a modal card are stored as a state value + // That's important for modal abilities with modes that can only selected once while the object stays in its zone + @SuppressWarnings("unchecked") private Set getAlreadySelectedModes(Ability source, Game game) { - return (Set) game.getState().getValue(getKey(source, game)); + Set onceSelectedModes = new HashSet<>(); + for (UUID modeId : this.keySet()) { + Object exist = game.getState().getValue(getKey(source, game, modeId)); + if (exist != null) { + onceSelectedModes.add(modeId); + } + } + return onceSelectedModes; } - private String getKey(Ability source, Game game) { - return CardUtil.getObjectZoneString("selectedModes", source.getSourceId(), game, game.getState().getZoneChangeCounter(source.getSourceId()), false); + // creates the key the selected modes are saved with to the state values + private String getKey(Ability source, Game game, UUID modeId) { + return source.getSourceId().toString() + game.getState().getZoneChangeCounter(source.getSourceId()) + modeId.toString(); } + /** + * Returns all (still) available modes of the ability + * + * @param source + * @param game + * @return + */ public List getAvailableModes(Ability source, Game game) { List availableModes = new ArrayList<>(); Set nonAvailableModes; diff --git a/Mage/src/main/java/mage/abilities/StaticAbility.java b/Mage/src/main/java/mage/abilities/StaticAbility.java index bbeee56b6a9..1930634dbe8 100644 --- a/Mage/src/main/java/mage/abilities/StaticAbility.java +++ b/Mage/src/main/java/mage/abilities/StaticAbility.java @@ -56,6 +56,9 @@ public abstract class StaticAbility extends AbilityImpl { if (game.getShortLivingLKI(getSourceId(), zone)) { return true; // maybe this can be a problem if effects removed the ability from the object } + if (game.getPermanentEntering(getSourceId()) != null && zone.equals(Zone.BATTLEFIELD)) { + return true; // abilities of permanents entering battlefield are countes as on battlefield + } return super.isInUseableZone(game, source, event); } diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java index c066f8ef090..967b22938e4 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java @@ -41,6 +41,7 @@ import mage.cards.Card; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.events.NumberOfTriggersEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -124,7 +125,12 @@ public class TriggeredAbilities extends ConcurrentHashMap 0) { + sumPower += powerToAdd; + } if (sumPower >= value) { return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/HeroicAbility.java b/Mage/src/main/java/mage/abilities/keyword/HeroicAbility.java index bbd8ed567db..083a80484b7 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HeroicAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HeroicAbility.java @@ -1,4 +1,4 @@ - /* +/* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -82,7 +82,8 @@ public class HeroicAbility extends TriggeredAbilityImpl { private boolean checkSpell(Spell spell, Game game) { if (spell != null) { SpellAbility sa = spell.getSpellAbility(); - for (Mode mode : sa.getModes().getSelectedModes()) { + for (UUID modeId : sa.getModes().getSelectedModes()) { + Mode mode = sa.getModes().get(modeId); for (Target target : mode.getTargets()) { if (!target.isNotTarget() && target.getTargets().contains(this.getSourceId())) { return true; diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index d890e5561dc..d4a18701215 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -29,7 +29,6 @@ package mage.cards; import java.lang.reflect.Constructor; import java.util.*; - import mage.MageObject; import mage.MageObjectImpl; import mage.Mana; @@ -53,10 +52,8 @@ import mage.game.command.Commander; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentCard; import mage.game.stack.Spell; import mage.game.stack.StackObject; -import mage.players.Player; import mage.util.GameLog; import mage.watchers.Watcher; import org.apache.log4j.Logger; @@ -315,11 +312,11 @@ public abstract class CardImpl extends MageObjectImpl implements Card { public String getTokenSetCode() { return tokenSetCode; } - + @Override public String getTokenDescriptor() { return tokenDescriptor; - } + } @Override public List getMana() { @@ -342,22 +339,29 @@ public abstract class CardImpl extends MageObjectImpl implements Card { Zone fromZone = game.getState().getZone(objectId); ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, ownerId, fromZone, toZone, appliedEffects); ZoneChangeInfo zoneChangeInfo; - if (toZone == Zone.LIBRARY) { - zoneChangeInfo = new ZoneChangeInfo.Library(event, flag /* put on top */); - } else if (toZone == Zone.BATTLEFIELD) { - zoneChangeInfo = new ZoneChangeInfo.Battlefield(event, flag /* comes into play tapped */); - } else { - zoneChangeInfo = new ZoneChangeInfo(event); + if (null != toZone) { + switch (toZone) { + case LIBRARY: + zoneChangeInfo = new ZoneChangeInfo.Library(event, flag /* put on top */); + break; + case BATTLEFIELD: + zoneChangeInfo = new ZoneChangeInfo.Battlefield(event, flag /* comes into play tapped */); + break; + default: + zoneChangeInfo = new ZoneChangeInfo(event); + break; + } + return ZonesHandler.moveCard(zoneChangeInfo, game); } - return ZonesHandler.moveCard(zoneChangeInfo, game); + return false; } @Override public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) { Card mainCard = getMainCard(); ZoneChangeEvent event = new ZoneChangeEvent(mainCard.getId(), ability.getId(), controllerId, fromZone, Zone.STACK); - ZoneChangeInfo.Stack info = - new ZoneChangeInfo.Stack(event, new Spell(this, ability.copy(), controllerId, event.getFromZone())); + ZoneChangeInfo.Stack info + = new ZoneChangeInfo.Stack(event, new Spell(this, ability.copy(), controllerId, event.getFromZone())); return ZonesHandler.cast(info, game); } @@ -449,6 +453,9 @@ public abstract class CardImpl extends MageObjectImpl implements Card { } else if (game.getPlayer(ownerId).getSideboard().contains(this.getId())) { game.getPlayer(ownerId).getSideboard().remove(this.getId()); removed = true; + } else if (game.getPhase() == null) { + // E.g. Commander of commander game + removed = true; } break; case BATTLEFIELD: // for sacrificing permanents or putting to library @@ -461,7 +468,9 @@ public abstract class CardImpl extends MageObjectImpl implements Card { break; } if (removed) { - game.rememberLKI(lkiObject != null ? lkiObject.getId() : objectId, fromZone, lkiObject != null ? lkiObject : this); + if (!fromZone.equals(Zone.OUTSIDE)) { + game.rememberLKI(lkiObject != null ? lkiObject.getId() : objectId, fromZone, lkiObject != null ? lkiObject : this); + } } else { logger.warn("Couldn't find card in fromZone, card=" + getIdName() + ", fromZone=" + fromZone); } @@ -598,7 +607,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { returnCode = false; } } - if(finalAmount > 0) { + if (finalAmount > 0) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERS_ADDED, objectId, getControllerOrOwner(), counter.getName(), amount)); } } else { @@ -656,7 +665,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { } return super.getColor(game); } - + @Override public List getSubtype(Game game) { if (game != null) { diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java index 47bfd2ed07c..3653b268a78 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/NumberOfTargetsPredicate.java @@ -27,6 +27,7 @@ */ package mage.filter.predicate.mageobject; +import java.util.UUID; import mage.MageObject; import mage.abilities.Mode; import mage.filter.predicate.Predicate; @@ -51,7 +52,8 @@ public class NumberOfTargetsPredicate implements Predicate { StackObject stackObject = game.getState().getStack().getStackObject(input.getId()); if (stackObject != null) { int numberOfTargets = 0; - for (Mode mode : stackObject.getStackAbility().getModes().getSelectedModes()) { + for (UUID modeId : stackObject.getStackAbility().getModes().getSelectedModes()) { + Mode mode = stackObject.getStackAbility().getModes().get(modeId); for (Target target : mode.getTargets()) { numberOfTargets += target.getTargets().size(); } diff --git a/Mage/src/main/java/mage/filter/predicate/other/TargetsPermanentPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/TargetsPermanentPredicate.java index aad5bb551b6..7dc12d7984d 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/TargetsPermanentPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/TargetsPermanentPredicate.java @@ -54,7 +54,8 @@ public class TargetsPermanentPredicate implements ObjectSourcePlayerPredicate input, Game game) { StackObject object = game.getStack().getStackObject(input.getObject().getId()); if (object != null) { - for (Mode mode : object.getStackAbility().getModes().getSelectedModes()) { + for (UUID modeId : object.getStackAbility().getModes().getSelectedModes()) { + Mode mode = object.getStackAbility().getModes().get(modeId); for (Target target : mode.getTargets()) { for (UUID targetId : target.getTargets()) { Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 2e48c954ce0..eb553340685 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -314,7 +314,8 @@ public class GameState implements Serializable, Copyable { for (StackObject spell : stack) { sb.append(spell.getControllerId()).append(spell.getName()); sb.append(spell.getStackAbility().toString()); - for (Mode mode : spell.getStackAbility().getModes().getSelectedModes()) { + for (UUID modeId : spell.getStackAbility().getModes().getSelectedModes()) { + Mode mode = spell.getStackAbility().getModes().get(modeId); if (!mode.getTargets().isEmpty()) { sb.append("targets"); for (Target target : mode.getTargets()) { @@ -366,7 +367,8 @@ public class GameState implements Serializable, Copyable { for (StackObject spell : stack) { sb.append(spell.getControllerId()).append(spell.getName()); sb.append(spell.getStackAbility().toString()); - for (Mode mode : spell.getStackAbility().getModes().getSelectedModes()) { + for (UUID modeId : spell.getStackAbility().getModes().getSelectedModes()) { + Mode mode = spell.getStackAbility().getModes().get(modeId); if (!mode.getTargets().isEmpty()) { sb.append("targets"); for (Target target : mode.getTargets()) { @@ -784,7 +786,8 @@ public class GameState implements Serializable, Copyable { public void addAbility(Ability ability, MageObject attachedTo) { if (ability instanceof StaticAbility) { - for (Mode mode : ability.getModes().getSelectedModes()) { + for (UUID modeId : ability.getModes().getSelectedModes()) { + Mode mode = ability.getModes().get(modeId); for (Effect effect : mode.getEffects()) { if (effect instanceof ContinuousEffect) { addEffect((ContinuousEffect) effect, ability); @@ -806,7 +809,8 @@ public class GameState implements Serializable, Copyable { */ public void addAbility(Ability ability, UUID sourceId, Card attachedTo) { if (ability instanceof StaticAbility) { - for (Mode mode : ability.getModes().getSelectedModes()) { + for (UUID modeId : ability.getModes().getSelectedModes()) { + Mode mode = ability.getModes().get(modeId); for (Effect effect : mode.getEffects()) { if (effect instanceof ContinuousEffect) { addEffect((ContinuousEffect) effect, sourceId, ability); diff --git a/Mage/src/main/java/mage/game/ZonesHandler.java b/Mage/src/main/java/mage/game/ZonesHandler.java index 6a53736d4a7..a57df07facd 100644 --- a/Mage/src/main/java/mage/game/ZonesHandler.java +++ b/Mage/src/main/java/mage/game/ZonesHandler.java @@ -59,7 +59,9 @@ public class ZonesHandler { } for (ZoneChangeInfo zoneChangeInfo : zoneChangeInfos) { placeInDestinationZone(zoneChangeInfo, game); - game.addSimultaneousEvent(zoneChangeInfo.event); + if (game.getPhase() != null) { // moving cards to zones before game started does not need events + game.addSimultaneousEvent(zoneChangeInfo.event); + } } return zoneChangeInfos; } diff --git a/Mage/src/main/java/mage/game/events/EntersTheBattlefieldEvent.java b/Mage/src/main/java/mage/game/events/EntersTheBattlefieldEvent.java index 34140839c3d..c4948e4b6e0 100644 --- a/Mage/src/main/java/mage/game/events/EntersTheBattlefieldEvent.java +++ b/Mage/src/main/java/mage/game/events/EntersTheBattlefieldEvent.java @@ -43,17 +43,17 @@ public class EntersTheBattlefieldEvent extends GameEvent { private Permanent target; public EntersTheBattlefieldEvent(Permanent target, UUID sourceId, UUID playerId, Zone fromZone) { - this(target, sourceId, playerId, fromZone, null); + super(EventType.ENTERS_THE_BATTLEFIELD, target.getId(), sourceId, playerId); + this.fromZone = fromZone; + this.target = target; } public EntersTheBattlefieldEvent(Permanent target, UUID sourceId, UUID playerId, Zone fromZone, ArrayList appliedEffects) { super(EventType.ENTERS_THE_BATTLEFIELD, target.getId(), sourceId, playerId); this.fromZone = fromZone; - this.target = target; if (appliedEffects != null) { this.appliedEffects = appliedEffects; } - this.amount = 1; // Number of times to trigger (Panharmonicon can change that value) } public Zone getFromZone() { diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index a984f80cbad..9e2626b6015 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -266,6 +266,7 @@ public class GameEvent implements Serializable { REGENERATE, REGENERATED, CHANGE_COLOR, COLOR_CHANGED, + NUMBER_OF_TRIGGERS, //combat events COMBAT_DAMAGE_APPLIED, SELECTED_ATTACKER, SELECTED_BLOCKER; diff --git a/Mage/src/main/java/mage/game/events/NumberOfTriggersEvent.java b/Mage/src/main/java/mage/game/events/NumberOfTriggersEvent.java new file mode 100644 index 00000000000..21346ce714e --- /dev/null +++ b/Mage/src/main/java/mage/game/events/NumberOfTriggersEvent.java @@ -0,0 +1,50 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.game.events; + +import java.util.UUID; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class NumberOfTriggersEvent extends GameEvent { + + private final GameEvent sourceEvent; + + public NumberOfTriggersEvent(UUID controllerOfAbilityId, UUID sourceOfTrigger, GameEvent sourceEvent) { + super(EventType.NUMBER_OF_TRIGGERS, null, sourceOfTrigger, controllerOfAbilityId); + this.sourceEvent = sourceEvent; + this.amount = 1; // Number of times to trigger. Panharmonicon can change this. + } + + public GameEvent getSourceEvent() { + return sourceEvent; + } +} diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index cb4d9d8a211..fa4319cbecc 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -881,12 +881,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { EntersTheBattlefieldEvent event = new EntersTheBattlefieldEvent(this, sourceId, getControllerId(), fromZone); if (!game.replaceEvent(event)) { if (fireEvent) { - // Trigger multiple times with Panharmonicon. - for (int i = 0; i < event.getAmount(); i++) { - game.addSimultaneousEvent(event); - } + game.addSimultaneousEvent(event); + return true; } - return true; } return false; } diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index b6bc9b98356..f587a9ed7ab 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -210,7 +210,8 @@ public class Spell extends StackObjImpl implements Card { if (notTargeted || legalParts) { for (SpellAbility spellAbility : this.spellAbilities) { if (spellAbilityHasLegalParts(spellAbility, game)) { - for (Mode mode : spellAbility.getModes().getSelectedModes()) { + for (UUID modeId : spellAbility.getModes().getSelectedModes()) { + Mode mode = spellAbility.getModes().get(modeId); spellAbility.getModes().setActiveMode(mode); if (mode.getTargets().stillLegal(spellAbility, game)) { if (!spellAbility.getSpellAbilityType().equals(SpellAbilityType.SPLICE)) { @@ -283,7 +284,8 @@ public class Spell extends StackObjImpl implements Card { private boolean hasTargets(SpellAbility spellAbility, Game game) { if (spellAbility.getModes().getSelectedModes().size() > 1) { - for (Mode mode : spellAbility.getModes().getSelectedModes()) { + for (UUID modeId : spellAbility.getModes().getSelectedModes()) { + Mode mode = spellAbility.getModes().get(modeId); if (!mode.getTargets().isEmpty()) { return true; } @@ -299,7 +301,8 @@ public class Spell extends StackObjImpl implements Card { if (spellAbility.getModes().getSelectedModes().size() > 1) { boolean targetedMode = false; boolean legalTargetedMode = false; - for (Mode mode : spellAbility.getModes().getSelectedModes()) { + for (UUID modeId : spellAbility.getModes().getSelectedModes()) { + Mode mode = spellAbility.getModes().get(modeId); if (mode.getTargets().size() > 0) { targetedMode = true; if (mode.getTargets().stillLegal(spellAbility, game)) { diff --git a/Mage/src/main/java/mage/game/stack/StackObjImpl.java b/Mage/src/main/java/mage/game/stack/StackObjImpl.java index 2286fd06ae5..d37d6f78a97 100644 --- a/Mage/src/main/java/mage/game/stack/StackObjImpl.java +++ b/Mage/src/main/java/mage/game/stack/StackObjImpl.java @@ -117,7 +117,8 @@ public abstract class StackObjImpl implements StackObject { } for (Ability ability : objectAbilities) { // Some spells can have more than one mode - for (Mode mode : ability.getModes().getSelectedModes()) { + for (UUID modeId : ability.getModes().getSelectedModes()) { + Mode mode = ability.getModes().get(modeId); ability.getModes().setActiveMode(mode); oldTargetDescription.append(ability.getTargetDescription(mode.getTargets(), game)); for (Target target : mode.getTargets()) { @@ -210,8 +211,7 @@ public abstract class StackObjImpl implements StackObject { again = true; } } else // if possible add the alternate Target - it may not be included in the old definition nor in the already selected targets of the new definition - { - if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) { + if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) { if (targetController.isHuman()) { if (targetController.chooseUse(Outcome.Benefit, "This target was already selected from origin spell. Reset to original target?", ability, game)) { // use previous target no target was selected @@ -240,7 +240,6 @@ public abstract class StackObjImpl implements StackObject { // valid target was selected, add it to the new target definition newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, false); } - } } while (again && targetController.canRespond()); } } // keep the target diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index a0ea1227c58..8b5ca4049d4 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -863,8 +863,9 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean putCardsOnBottomOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder) { - if (!cards.isEmpty()) { + public boolean putCardsOnBottomOfLibrary(Cards cardsToLibrary, Game game, Ability source, boolean anyOrder) { + if (!cardsToLibrary.isEmpty()) { + Cards cards = new CardsImpl(cardsToLibrary); // prevent possible ConcurrentModificationException if (!anyOrder) { for (UUID objectId : cards) { moveObjectToLibrary(objectId, source == null ? null : source.getSourceId(), game, false, false); @@ -996,6 +997,10 @@ public abstract class PlayerImpl implements Player, Serializable { Zone fromZone = game.getState().getZone(card.getMainCard().getId()); card.cast(game, fromZone, ability, playerId); Spell spell = game.getStack().getSpell(ability.getId()); + if (spell == null) { + logger.error("Got no spell from stack. ability: " + ability.getRule()); + return false; + } // some effects set sourceId to cast without paying mana costs or other costs if (ability.getSourceId().equals(getCastSourceIdWithAlternateMana())) { Ability spellAbility = spell.getSpellAbility(); @@ -2865,7 +2870,7 @@ public abstract class PlayerImpl implements Player, Serializable { for (Mode mode : option.getModes().values()) { Ability newOption = option.copy(); newOption.getModes().getSelectedModes().clear(); - newOption.getModes().getSelectedModes().add(mode); + newOption.getModes().getSelectedModes().add(mode.getId()); newOption.getModes().setActiveMode(mode); if (newOption.getTargets().getUnchosen().size() > 0) { if (newOption.getManaCosts().getVariableCosts().size() > 0) { diff --git a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalker.java b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalker.java index 3e58a0523eb..c075c54d4fa 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalker.java +++ b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalker.java @@ -25,10 +25,13 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.target.common; +import java.util.UUID; +import mage.abilities.Ability; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; +import mage.game.Game; +import mage.players.Player; import mage.target.TargetPermanent; /** @@ -38,7 +41,7 @@ import mage.target.TargetPermanent; public class TargetCreatureOrPlaneswalker extends TargetPermanent { public TargetCreatureOrPlaneswalker() { - this(1, 1 ,new FilterCreatureOrPlaneswalkerPermanent(), false); + this(1, 1, new FilterCreatureOrPlaneswalkerPermanent(), false); } public TargetCreatureOrPlaneswalker(int minNumTargets, int maxNumTargets, FilterCreatureOrPlaneswalkerPermanent filter, boolean notTarget) { @@ -55,4 +58,16 @@ public class TargetCreatureOrPlaneswalker extends TargetPermanent { return new TargetCreatureOrPlaneswalker(this); } + @Override + public boolean isLegal(Ability source, Game game) { + for (UUID playerId : targets.keySet()) { + Player targetPlayer = game.getPlayer(playerId); + if (targetPlayer != null) { + // there seems to be no possibility to add more predicates for theplayer so return here true + return true; + } + } + return super.isLegal(source, game); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java b/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java new file mode 100644 index 00000000000..22896eab0d9 --- /dev/null +++ b/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java @@ -0,0 +1,117 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.target.common; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.constants.Zone; +import mage.filter.Filter; +import mage.filter.FilterAbility; +import mage.game.Game; +import mage.game.stack.StackObject; +import mage.target.TargetObject; + +/** + * + * @author Styxo + */ +public class TargetTriggeredAbility extends TargetObject { + + public TargetTriggeredAbility() { + this.minNumberOfTargets = 1; + this.maxNumberOfTargets = 1; + this.zone = Zone.STACK; + this.targetName = "target triggered ability you control"; + } + + public TargetTriggeredAbility(final TargetTriggeredAbility target) { + super(target); + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + if (source != null && source.getSourceId().equals(id)) { + return false; + } + + StackObject stackObject = game.getStack().getStackObject(id); + return stackObject.getStackAbility() != null + && (stackObject.getStackAbility() instanceof TriggeredAbility) + && source != null + && stackObject.getStackAbility().getControllerId().equals(source.getControllerId()); + } + + @Override + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { + return canChoose(sourceControllerId, game); + } + + @Override + public boolean canChoose(UUID sourceControllerId, Game game) { + for (StackObject stackObject : game.getStack()) { + if (stackObject.getStackAbility() != null + && stackObject.getStackAbility() instanceof TriggeredAbility + && stackObject.getStackAbility().getControllerId().equals(sourceControllerId)) { + return true; + } + } + return false; + } + + @Override + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { + return possibleTargets(sourceControllerId, game); + } + + @Override + public Set possibleTargets(UUID sourceControllerId, Game game) { + Set possibleTargets = new HashSet<>(); + for (StackObject stackObject : game.getStack()) { + if (stackObject.getStackAbility() != null + && stackObject.getStackAbility() instanceof TriggeredAbility + && stackObject.getStackAbility().getControllerId().equals(sourceControllerId)) { + possibleTargets.add(stackObject.getStackAbility().getId()); + } + } + return possibleTargets; + } + + @Override + public TargetTriggeredAbility copy() { + return new TargetTriggeredAbility(this); + } + + @Override + public Filter getFilter() { + return new FilterAbility(); + } + +} diff --git a/Mage/src/main/java/mage/util/TargetAddress.java b/Mage/src/main/java/mage/util/TargetAddress.java index 57bdddc23b4..80c5724676d 100644 --- a/Mage/src/main/java/mage/util/TargetAddress.java +++ b/Mage/src/main/java/mage/util/TargetAddress.java @@ -70,7 +70,7 @@ public class TargetAddress { protected Iterator spellAbilityIterator; protected Integer lastSpellAbilityIndex = null; - protected Iterator modeIterator = null; + protected Iterator modeIterator = null; protected Modes modes = null; protected UUID lastMode = null; protected Iterator targetIterator = null; @@ -127,7 +127,7 @@ public class TargetAddress { } if (modeIterator != null && modeIterator.hasNext()) { - lastMode = modeIterator.next().getId(); + lastMode = modeIterator.next(); targetIterator = modes.get(lastMode).getTargets().iterator(); } else { lastMode = null;