From f8a1b327b4749d22d0c26bd2210300e674ff2cc2 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Fri, 11 Jul 2014 10:49:00 +0400 Subject: [PATCH 01/20] [UI] Order of cards in hand is changable --- .../src/main/java/mage/client/cards/Card.java | 59 +--- .../main/java/mage/client/cards/Cards.java | 91 +++--- .../main/java/mage/client/game/HandPanel.java | 10 +- .../plugins/adapters/MageActionCallback.java | 291 ++++++++++-------- .../mage/client/util/gui/ArrowBuilder.java | 1 + .../java/mage/client/util/gui/ArrowUtil.java | 116 +++++++ .../java/org/mage/card/arcane/CardPanel.java | 56 +++- Mage.Common/src/mage/cards/MageCard.java | 8 +- .../src/mage/cards/action/ActionCallback.java | 1 + .../mage/cards/action/impl/EmptyCallback.java | 5 + 10 files changed, 399 insertions(+), 239 deletions(-) create mode 100644 Mage.Client/src/main/java/mage/client/util/gui/ArrowUtil.java diff --git a/Mage.Client/src/main/java/mage/client/cards/Card.java b/Mage.Client/src/main/java/mage/client/cards/Card.java index 24bade0d9df..0057f63b9c7 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Card.java +++ b/Mage.Client/src/main/java/mage/client/cards/Card.java @@ -34,50 +34,12 @@ package mage.client.cards; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.event.ComponentEvent; -import java.awt.event.ComponentListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import javax.swing.JScrollPane; -import javax.swing.Popup; -import javax.swing.PopupFactory; -import javax.swing.text.BadLocationException; -import javax.swing.text.Style; -import javax.swing.text.StyleConstants; -import javax.swing.text.StyleContext; -import javax.swing.text.StyledDocument; import mage.cards.CardDimensions; import mage.cards.MagePermanent; import mage.cards.Sets; import mage.cards.TextPopup; import mage.cards.action.ActionCallback; import mage.client.MageFrame; -import static mage.client.constants.Constants.CONTENT_MAX_XOFFSET; -import static mage.client.constants.Constants.FRAME_MAX_HEIGHT; -import static mage.client.constants.Constants.FRAME_MAX_WIDTH; -import static mage.client.constants.Constants.NAME_FONT_MAX_SIZE; -import static mage.client.constants.Constants.NAME_MAX_YOFFSET; -import static mage.client.constants.Constants.POWBOX_TEXT_MAX_LEFT; -import static mage.client.constants.Constants.POWBOX_TEXT_MAX_TOP; -import static mage.client.constants.Constants.SYMBOL_MAX_XOFFSET; -import static mage.client.constants.Constants.SYMBOL_MAX_YOFFSET; -import static mage.client.constants.Constants.TYPE_MAX_YOFFSET; import mage.client.game.PlayAreaPanel; import mage.client.util.Config; import mage.client.util.DefaultActionCallback; @@ -86,11 +48,18 @@ import mage.client.util.gui.ArrowBuilder; import mage.constants.CardType; import mage.constants.EnlargeMode; import mage.remote.Session; -import mage.view.AbilityView; -import mage.view.CardView; -import mage.view.CounterView; -import mage.view.PermanentView; -import mage.view.StackAbilityView; +import mage.view.*; + +import javax.swing.*; +import javax.swing.text.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static mage.client.constants.Constants.*; /** * @@ -557,4 +526,8 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis @Override public void setSelected(boolean selected) { } + + @Override + public void setCardAreaRef(JPanel cardArea) { + } } diff --git a/Mage.Client/src/main/java/mage/client/cards/Cards.java b/Mage.Client/src/main/java/mage/client/cards/Cards.java index 69491e22978..8387066fe39 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Cards.java +++ b/Mage.Client/src/main/java/mage/client/cards/Cards.java @@ -39,14 +39,14 @@ import mage.client.plugins.impl.Plugins; import mage.client.util.CardsViewUtil; import mage.client.util.Config; import mage.view.*; +import org.apache.log4j.Logger; +import org.mage.card.arcane.CardPanel; import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; import java.awt.*; import java.util.*; import java.util.Map.Entry; -import mage.cards.action.ActionCallback; -import mage.client.plugins.adapters.MageActionCallback; -import org.apache.log4j.Logger; /** * @@ -61,6 +61,8 @@ public class Cards extends javax.swing.JPanel { private static final int GAP_X = 5; private String zone; + private static final Border emptyBorder = new EmptyBorder(0,0,0,0); + /** * Defines whether component should be visible whenever there is no objects within. * True by default. @@ -82,18 +84,15 @@ public class Cards extends javax.swing.JPanel { if (!skipAddingScrollPane) { jScrollPane1.setOpaque(false); jScrollPane1.getViewport().setOpaque(false); + jScrollPane1.setBorder(emptyBorder); } if (Plugins.getInstance().isCardPluginLoaded()) { cardArea.setLayout(null); } + cardArea.setBorder(emptyBorder); } - public void cleanUp() { -// ActionCallback actionCallback = Plugins.getInstance().getActionCallback(); -// if (actionCallback instanceof MageActionCallback) { -// ((MageActionCallback) actionCallback).setCardPreviewComponent(null); -// } - } + public void cleanUp() {} /** * Sets components background color @@ -182,12 +181,13 @@ public class Cards extends javax.swing.JPanel { } if (changed) { - layoutCards(getCardDimension(), cards, order); + layoutCards(); } if (!isVisibleIfEmpty) { cardArea.setVisible(cards.size() > 0); } + sizeCards(getCardDimension()); this.revalidate(); this.repaint(); @@ -196,13 +196,13 @@ public class Cards extends javax.swing.JPanel { } public void sizeCards(Dimension cardDimension) { - cardArea.setPreferredSize(new Dimension((int)(cards.size() * (cardDimension.getWidth() + GAP_X)) + 5, (int)(cardDimension.getHeight()) + 20)); + cardArea.setPreferredSize(new Dimension((int)((cards.size()) * (cardDimension.getWidth() + GAP_X)) + 20, (int)(cardDimension.getHeight()) + 20)); cardArea.revalidate(); cardArea.repaint(); } private Dimension getCardDimension() { - if (cardDimension == null) { + if (cardDimension == null) { cardDimension = new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight); } return cardDimension; @@ -215,6 +215,19 @@ public class Cards extends javax.swing.JPanel { } cards.put(card.getId(), cardImg); cardArea.add(cardImg); + definePosition(cardImg); + cardImg.setCardAreaRef(cardArea); + } + + private void definePosition(MageCard card) { + int dx = 0; + for (Component comp: cardArea.getComponents()) { + if (!comp.equals(card)) { + dx = Math.max(dx, (int)comp.getLocation().getX()); + } + } + dx += ((CardPanel)card).getCardWidth() + GAP_X; + card.setLocation(dx, (int)card.getLocation().getY()); } private void removeCard(UUID cardId) { @@ -232,35 +245,7 @@ public class Cards extends javax.swing.JPanel { } private int countCards() { - int count = 0; - for (Component comp: cardArea.getComponents()) { - count++; - } - return count; - } - - private void layoutCards(Dimension dimension, Map cards, java.util.List order) { - if (Plugins.getInstance().isCardPluginLoaded()) { - int dx = GAP_X; - if (order != null) { - for (UUID cardId : order) { - MageCard card = cards.get(cardId); - if (card != null) { - card.setLocation(dx, 0); - card.setCardBounds(dx, 0, dimension.width, dimension.height); - dx += dimension.width + GAP_X; - } else { - System.err.println("[ERROR] Cards.java: couldn't find a card from ordered list!"); - } - } - } else { - for (MageCard card: cards.values()) { - card.setLocation(dx, 0); - card.setCardBounds(dx, 0, dimension.width, dimension.height); - dx += dimension.width + GAP_X; - } - } - } + return cardArea.getComponentCount(); } /** This method is called from within the constructor to @@ -311,7 +296,29 @@ public class Cards extends javax.swing.JPanel { public void setCardDimension(Dimension dimension) { this.cardDimension = dimension; - layoutCards(cardDimension, cards, null); + layoutCards(); + } + + private void layoutCards() { + java.util.List cards = new ArrayList(); + + for (Component component : cardArea.getComponents()) { + if (component instanceof CardPanel) { + cards.add((CardPanel)component); + } + } + Collections.sort(cards, new Comparator() { + @Override + public int compare(CardPanel cp1, CardPanel cp2) { + return Integer.valueOf(cp1.getLocation().x).compareTo(Integer.valueOf(cp2.getLocation().x)); + } + }); + + int dx = 0; + for (Component component : cards) { + component.setLocation(dx, component.getLocation().y); + dx += ((CardPanel) component).getCardWidth() + GAP_X; + } } public void setZone(String zone) { diff --git a/Mage.Client/src/main/java/mage/client/game/HandPanel.java b/Mage.Client/src/main/java/mage/client/game/HandPanel.java index 5cbf9803f39..756c4fa5e78 100644 --- a/Mage.Client/src/main/java/mage/client/game/HandPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/HandPanel.java @@ -5,13 +5,13 @@ import mage.client.cards.BigCard; import mage.client.dialog.PreferencesDialog; import mage.client.util.Config; import mage.constants.Zone; +import mage.view.CardsView; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import java.awt.*; import java.util.UUID; -import mage.view.CardsView; public class HandPanel extends JPanel { @@ -31,12 +31,13 @@ public class HandPanel extends JPanel { public void initComponents() { hand = new mage.client.cards.Cards(true); hand.setCardDimension(getHandCardDimension()); + jPanel = new JPanel(); jScrollPane1 = new JScrollPane(jPanel); jScrollPane1.getViewport().setBackground(new Color(0,0,0,0)); jPanel.setLayout(new GridBagLayout()); // centers hand - jPanel.setBackground(new Color(0,0,0,0)); + jPanel.setBackground(new Color(0, 0, 0, 0)); jPanel.add(hand); setOpaque(false); @@ -47,12 +48,13 @@ public class HandPanel extends JPanel { jScrollPane1.setBorder(emptyBorder); jScrollPane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); jScrollPane1.getHorizontalScrollBar().setUnitIncrement(8); + jScrollPane1.setViewportBorder(emptyBorder); setLayout(new BorderLayout()); add(jScrollPane1, BorderLayout.CENTER); hand.setHScrollSpeed(8); - hand.setBackgroundColor(new Color(0, 0, 0, 100)); + hand.setBackgroundColor(new Color(0, 0, 0, 0)); hand.setVisibleIfEmpty(false); hand.setBorder(emptyBorder); hand.setZone(Zone.HAND.toString()); @@ -87,7 +89,7 @@ public class HandPanel extends JPanel { private JPanel jPanel; private JScrollPane jScrollPane1; - private Border emptyBorder = new EmptyBorder(0,0,0,0); + private static final Border emptyBorder = new EmptyBorder(0,0,0,0); private mage.client.cards.Cards hand; } diff --git a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java index fc148a0636b..8920ba7028b 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java +++ b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java @@ -1,19 +1,17 @@ package mage.client.plugins.adapters; import mage.cards.MageCard; -import mage.cards.MagePermanent; import mage.cards.action.ActionCallback; import mage.cards.action.TransferData; import mage.client.MageFrame; import mage.client.cards.BigCard; import mage.client.components.MageComponents; import mage.client.dialog.PreferencesDialog; -import mage.client.game.PlayAreaPanel; -import mage.client.game.PlayerPanelExt; import mage.client.plugins.impl.Plugins; import mage.client.util.DefaultActionCallback; import mage.client.util.ImageHelper; import mage.client.util.gui.ArrowBuilder; +import mage.client.util.gui.ArrowUtil; import mage.client.util.gui.GuiDisplayUtil; import mage.components.CardInfoPane; import mage.constants.EnlargeMode; @@ -21,10 +19,9 @@ import mage.remote.Session; import mage.utils.ThreadUtils; import mage.view.CardView; import mage.view.PermanentView; -import mage.view.PlayerView; -import mage.view.SimpleCardsView; import org.apache.log4j.Logger; import org.jdesktop.swingx.JXPanel; +import org.mage.card.arcane.CardPanel; import org.mage.plugins.card.images.ImageCache; import javax.swing.*; @@ -32,8 +29,8 @@ import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.image.BufferedImage; +import java.util.*; import java.util.List; -import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -43,11 +40,15 @@ import java.util.concurrent.TimeUnit; * Class that handles the callbacks from the card panels to mage to display big card * images from the cards the mouse hovers on. Also handles tooltip text window. * - * @author Nantuko + * @author Nantuko, noxx */ public class MageActionCallback implements ActionCallback { private static final Logger logger = Logger.getLogger(ActionCallback.class); + public static final int GAP_X = 5; + public static final double COMPARE_GAP_X = 30; + public static final int GO_DOWN_ON_DRAG_Y_OFFSET = 0; + public static final int MIN_X_OFFSET_REQUIRED = 25; private Popup popup; private JPopupMenu jPopupMenu; @@ -69,6 +70,12 @@ public class MageActionCallback implements ActionCallback { private static final ScheduledExecutorService timeoutExecutor = Executors.newScheduledThreadPool(1); private ScheduledFuture hideTimeout; + private CardView currentCard; + private boolean isDragging; + private Point initialCardPos; + private Point initialMousePos; + private Set cardPanels = new HashSet(); + public MageActionCallback() { enlargeMode = EnlargeMode.NORMAL; } @@ -90,21 +97,6 @@ public class MageActionCallback implements ActionCallback { public void mouseClicked(MouseEvent e, TransferData data) { } - @Override - public void mousePressed(MouseEvent e, TransferData data) { - data.component.requestFocusInWindow(); - // Closes popup & enlarged view if a card/Permanent is selected - hidePopup(); - } - - @Override - public void mouseReleased(MouseEvent e, TransferData data) { - data.component.requestFocusInWindow(); - defaultCallback.mouseClicked(e, data.gameId, session, data.card); - // Closes popup & enlarged view if a card/Permanent is selected - hidePopup(); - } - @Override public void mouseEntered(MouseEvent e, final TransferData data) { hidePopup(); @@ -116,109 +108,18 @@ public class MageActionCallback implements ActionCallback { Component parentComponent = SwingUtilities.getRoot(data.component); Point parentPoint = parentComponent.getLocationOnScreen(); - drawArrowsForTargets(data, parentPoint); - drawArrowsForSource(data, parentPoint); - drawArrowsForPairedCards(data, parentPoint); - drawArrowsForEnchantPlayers(data, parentPoint); + if (data.locationOnScreen == null) { + data.locationOnScreen = data.component.getLocationOnScreen(); + } + + ArrowUtil.drawArrowsForTargets(data, parentPoint); + ArrowUtil.drawArrowsForSource(data, parentPoint); + ArrowUtil.drawArrowsForPairedCards(data, parentPoint); + ArrowUtil.drawArrowsForEnchantPlayers(data, parentPoint); showPopup(data, parentComponent, parentPoint); } - private void drawArrowsForPairedCards(TransferData data, Point parentPoint) { - if (data.card.getPairedCard() != null) { - Point me = new Point(data.locationOnScreen); - me.translate(-parentPoint.x, -parentPoint.y); - UUID uuid = data.card.getPairedCard(); - for (PlayAreaPanel pa : MageFrame.getGame(data.gameId).getPlayers().values()) { - MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); - if (permanent != null) { - Point target = permanent.getLocationOnScreen(); - target.translate(-parentPoint.x, -parentPoint.y); - ArrowBuilder.getBuilder().addArrow(data.gameId, (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() + 10, Color.green, ArrowBuilder.Type.PAIRED); - } - } - } - } - - private void drawArrowsForEnchantPlayers(TransferData data, Point parentPoint) { - if (data.gameId != null && MageFrame.getGame(data.gameId) != null) { - for (PlayAreaPanel pa : MageFrame.getGame(data.gameId).getPlayers().values()) { - PlayerPanelExt playAreaPanel = pa.getPlayerPanel(); - if (playAreaPanel != null && playAreaPanel.getPlayer() != null && playAreaPanel.getPlayer().hasAttachments()) { - Point me = new Point(data.locationOnScreen); - me.translate(-parentPoint.x, -parentPoint.y); - for (UUID attachmentId : playAreaPanel.getPlayer().getAttachments()) { - if (attachmentId.equals(data.card.getId())) { - Point player = pa.getLocationOnScreen(); - player.translate(-parentPoint.x, -parentPoint.y); - ArrowBuilder.getBuilder().addArrow(data.gameId,(int) me.getX() + 35, (int) me.getY(), (int) player.getX() + 40, (int) player.getY() - 40, Color.magenta, ArrowBuilder.Type.ENCHANT_PLAYERS); - } - } - } - } - } - } - - private void drawArrowsForSource(TransferData data, Point parentPoint) { - if (data.card.isAbility()) { - Point me = new Point(data.locationOnScreen); - me.translate(-parentPoint.x, -parentPoint.y); - UUID uuid = data.card.getParentId(); - for (PlayAreaPanel pa : MageFrame.getGame(data.gameId).getPlayers().values()) { - MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); - if (permanent != null) { - Point source = permanent.getLocationOnScreen(); - source.translate(-parentPoint.x, -parentPoint.y); - ArrowBuilder.getBuilder().addArrow(data.gameId, (int) source.getX() + 40, (int) source.getY() + 10, (int) me.getX() + 35, (int) me.getY() + 20, Color.blue, ArrowBuilder.Type.SOURCE); - } - } - } - } - - private void drawArrowsForTargets(TransferData data, Point parentPoint) { - List targets = data.card.getTargets(); - if (targets == null) { - return; - } - - Point me = new Point(data.locationOnScreen); - me.translate(-parentPoint.x, -parentPoint.y); - for (UUID uuid : targets) { - - PlayAreaPanel p = MageFrame.getGame(data.gameId).getPlayers().get(uuid); - if (p != null) { - Point target = p.getLocationOnScreen(); - target.translate(-parentPoint.x, -parentPoint.y); - ArrowBuilder.getBuilder().addArrow(data.gameId, (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() - 40, Color.red, ArrowBuilder.Type.TARGET); - continue; - } - - for (PlayAreaPanel panel : MageFrame.getGame(data.gameId).getPlayers().values()) { - MagePermanent permanent = panel.getBattlefieldPanel().getPermanents().get(uuid); - if (permanent != null) { - Point target = permanent.getLocationOnScreen(); - target.translate(-parentPoint.x, -parentPoint.y); - ArrowBuilder.getBuilder().addArrow(data.gameId, (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() + 10, Color.red, ArrowBuilder.Type.TARGET); - continue; - } - - PlayerView view = panel.getPlayerPanel().getPlayer(); - if (view != null) { - SimpleCardsView graveyard = view.getGraveyard(); - if (graveyard.containsKey(uuid)) { - p = MageFrame.getGame(data.gameId).getPlayers().get(view.getPlayerId()); - if (p != null) { - Point target = p.getLocationOnScreen(); - target.translate(-parentPoint.x, -parentPoint.y); - int yOffset = p.isSmallMode() ? (PlayAreaPanel.PANEL_HEIGHT - PlayAreaPanel.PANEL_HEIGHT_SMALL) : 0; - ArrowBuilder.getBuilder().addArrow(data.gameId, (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 15, (int) target.getY() + 145 - yOffset, Color.red, ArrowBuilder.Type.TARGET); - } - } - } - } - } - } - private void showPopup(final TransferData data, final Component parentComponent, final Point parentPoint) { if (data.component != null) { String showTooltips = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_TOOLTIPS_ANY_ZONE, "true"); @@ -229,6 +130,12 @@ public class MageActionCallback implements ActionCallback { if (cardInfoPane == null) { PopupFactory factory = PopupFactory.getSharedInstance(); + if (data.locationOnScreen == null) { + if (data.component == null) { + return; + } + data.locationOnScreen = data.component.getLocationOnScreen(); + } popup = factory.getPopup(data.component, data.popupText, (int) data.locationOnScreen.getX() + data.popupOffsetX, (int) data.locationOnScreen.getY() + data.popupOffsetY + 40); popup.show(); // hack to get popup to resize to fit text @@ -256,6 +163,10 @@ public class MageActionCallback implements ActionCallback { ((CardInfoPane) popup2).setCard(data.card, popupContainer); + if (data.locationOnScreen == null) { + data.locationOnScreen = data.component.getLocationOnScreen(); + } + Point location = new Point((int) data.locationOnScreen.getX() + data.popupOffsetX - 40, (int) data.locationOnScreen.getY() + data.popupOffsetY - 40); location = GuiDisplayUtil.keepComponentInsideParent(location, parentPoint, popup2, parentComponent); location.translate(-parentPoint.x, -parentPoint.y); @@ -265,15 +176,15 @@ public class MageActionCallback implements ActionCallback { final Component c = MageFrame.getUI().getComponent(MageComponents.DESKTOP_PANE); SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (!popupTextWindowOpen || !enlargedWindowState.equals(EnlargedWindowState.CLOSED)) { - return; - } - popupContainer.setVisible(true); - c.repaint(); - } - } + @Override + public void run() { + if (!popupTextWindowOpen || !enlargedWindowState.equals(EnlargedWindowState.CLOSED)) { + return; + } + popupContainer.setVisible(true); + c.repaint(); + } + } ); } catch (InterruptedException e) { @@ -283,8 +194,127 @@ public class MageActionCallback implements ActionCallback { }); } + @Override + public void mousePressed(MouseEvent e, TransferData data) { + data.component.requestFocusInWindow(); + currentCard = data.card; + isDragging = false; + cardPanels.clear(); + Point mouse = new Point(e.getX(), e.getY()); + SwingUtilities.convertPointToScreen(mouse, data.component); + initialMousePos = new Point((int)mouse.getX(), (int)mouse.getY()); + initialCardPos = data.component.getLocation(); + // Closes popup & enlarged view if a card/Permanent is selected + hidePopup(); + } + + @Override + public void mouseReleased(MouseEvent e, TransferData transferData) { + int maxXOffset = 0; + if (isDragging) { + CardPanel card = ((CardPanel)transferData.component); + Point mouse = new Point(e.getX(), e.getY()); + SwingUtilities.convertPointToScreen(mouse, transferData.component); + int xOffset = card.getXOffset(card.getCardWidth()); + maxXOffset = Math.abs((int) (mouse.getX() - initialMousePos.x) - xOffset); + + } + if (maxXOffset > MIN_X_OFFSET_REQUIRED) { // we need this for protection from small card movements + CardPanel card = ((CardPanel)transferData.component); + for (Component component : card.getCardArea().getComponents()) { + if (component instanceof CardPanel) { + if (cardPanels.contains(component)) { + component.setLocation(component.getLocation().x, component.getLocation().y - GO_DOWN_ON_DRAG_Y_OFFSET); + } + } + } + sort(card, card.getCardArea(), true); + cardPanels.clear(); + } else { + transferData.component.requestFocusInWindow(); + defaultCallback.mouseClicked(e, transferData.gameId, session, transferData.card); + // Closes popup & enlarged view if a card/Permanent is selected + hidePopup(); + } + } + @Override public void mouseMoved(MouseEvent e, TransferData transferData) { + handlePopup(transferData); + } + + @Override + public void mouseDragged(MouseEvent e, TransferData transferData) { + CardPanel card = ((CardPanel)transferData.component); + if (card.getZone() == null || !card.getZone().equalsIgnoreCase("hand")) { + // drag'n'drop is allowed for HAND zone only + return; + } + currentCard = null; + isDragging = true; + Point p = card.getCardLocation(); + Point mouse = new Point(e.getX(), e.getY()); + SwingUtilities.convertPointToScreen(mouse, transferData.component); + int xOffset = card.getXOffset(card.getCardWidth()); + int newX = Math.max(initialCardPos.x + (int)(mouse.getX() - initialMousePos.x) - xOffset, 0); + card.setCardBounds( + newX, + p.y, + card.getCardWidth(), + card.getCardHeight()); + card.getCardArea().setComponentZOrder(card, 0); + sort(card, card.getCardArea(), false); + } + + private void sort(CardPanel card, JPanel container, boolean sortSource) { + java.util.List cards = new ArrayList(); + for (Component component : container.getComponents()) { + if (component instanceof CardPanel) { + if (!component.equals(card)) { + if (!cardPanels.contains(component)) { + component.setLocation(component.getLocation().x, component.getLocation().y + GO_DOWN_ON_DRAG_Y_OFFSET); + } + cardPanels.add((CardPanel)component); + } + cards.add((CardPanel)component); + } + } + sortLayout(cards, card, sortSource); + } + + private void sortLayout(List cards, CardPanel source, boolean includeSource) { + source.getLocation().x -= COMPARE_GAP_X; // this creates nice effect + + Collections.sort(cards, new Comparator() { + @Override + public int compare(CardPanel cp1, CardPanel cp2) { + return Integer.valueOf(cp1.getLocation().x).compareTo(cp2.getLocation().x); + } + }); + + int dx = 0; + boolean createdGapForSource = false; + for (Component component : cards) { + if (!includeSource) { + if (!component.equals(source)) { + component.setLocation(dx, component.getLocation().y); + dx += ((CardPanel) component).getCardWidth() + GAP_X; + // once dx is bigger than source's x position + // we need to create a gap for the source card + // but only once + if (!createdGapForSource && (dx + COMPARE_GAP_X) > source.getLocation().x) { + createdGapForSource = true; + dx += ((CardPanel) component).getCardWidth() + GAP_X; + } + } + } else { + component.setLocation(dx, component.getLocation().y); + dx += ((CardPanel) component).getCardWidth() + GAP_X; + } + } + } + + private void handlePopup(TransferData transferData) { if (!Plugins.getInstance().isCardPluginLoaded()) { return; } @@ -469,6 +499,7 @@ public class MageActionCallback implements ActionCallback { final Component popupContainer = MageFrame.getUI().getComponent(mageComponentCardPreviewContainer); Component cardPreviewPane = MageFrame.getUI().getComponent(mageComponentCardPreviewPane); if (cardPreviewPane != null) { + transferData.locationOnScreen = transferData.component.getLocationOnScreen(); Point location = new Point((int) transferData.locationOnScreen.getX() + transferData.popupOffsetX - 40, (int) transferData.locationOnScreen.getY() + transferData.popupOffsetY - 40); location = GuiDisplayUtil.keepComponentInsideParent(location, parentPoint, cardPreviewPane, parentComponent); location.translate(-parentPoint.x, -parentPoint.y); diff --git a/Mage.Client/src/main/java/mage/client/util/gui/ArrowBuilder.java b/Mage.Client/src/main/java/mage/client/util/gui/ArrowBuilder.java index 3e68d2b6d27..62c256105bb 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/ArrowBuilder.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/ArrowBuilder.java @@ -180,4 +180,5 @@ public class ArrowBuilder { arrowPanels.get(gameId).setVisible(true); } } + } diff --git a/Mage.Client/src/main/java/mage/client/util/gui/ArrowUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/ArrowUtil.java new file mode 100644 index 00000000000..cafc0bc7c08 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/ArrowUtil.java @@ -0,0 +1,116 @@ +package mage.client.util.gui; + +import mage.cards.MagePermanent; +import mage.cards.action.TransferData; +import mage.client.MageFrame; +import mage.client.game.PlayAreaPanel; +import mage.client.game.PlayerPanelExt; +import mage.view.PlayerView; +import mage.view.SimpleCardsView; + +import java.awt.*; +import java.util.*; + +/** + * @author noxx + */ +public class ArrowUtil { + + private ArrowUtil() {} + + public static void drawArrowsForPairedCards(TransferData data, Point parentPoint) { + if (data.card.getPairedCard() != null) { + Point me = new Point(data.locationOnScreen); + me.translate(-parentPoint.x, -parentPoint.y); + UUID uuid = data.card.getPairedCard(); + for (PlayAreaPanel pa : MageFrame.getGame(data.gameId).getPlayers().values()) { + MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); + if (permanent != null) { + Point target = permanent.getLocationOnScreen(); + target.translate(-parentPoint.x, -parentPoint.y); + ArrowBuilder.getBuilder().addArrow(data.gameId, (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() + 10, Color.green, ArrowBuilder.Type.PAIRED); + } + } + } + } + + public static void drawArrowsForEnchantPlayers(TransferData data, Point parentPoint) { + if (data.gameId != null && MageFrame.getGame(data.gameId) != null) { + for (PlayAreaPanel pa : MageFrame.getGame(data.gameId).getPlayers().values()) { + PlayerPanelExt playAreaPanel = pa.getPlayerPanel(); + if (playAreaPanel != null && playAreaPanel.getPlayer() != null && playAreaPanel.getPlayer().hasAttachments()) { + Point me = new Point(data.locationOnScreen); + me.translate(-parentPoint.x, -parentPoint.y); + for (UUID attachmentId : playAreaPanel.getPlayer().getAttachments()) { + if (attachmentId.equals(data.card.getId())) { + Point player = pa.getLocationOnScreen(); + player.translate(-parentPoint.x, -parentPoint.y); + ArrowBuilder.getBuilder().addArrow(data.gameId,(int) me.getX() + 35, (int) me.getY(), (int) player.getX() + 40, (int) player.getY() - 40, Color.magenta, ArrowBuilder.Type.ENCHANT_PLAYERS); + } + } + } + } + } + } + + public static void drawArrowsForSource(TransferData data, Point parentPoint) { + if (data.card.isAbility()) { + Point me = new Point(data.locationOnScreen); + me.translate(-parentPoint.x, -parentPoint.y); + UUID uuid = data.card.getParentId(); + for (PlayAreaPanel pa : MageFrame.getGame(data.gameId).getPlayers().values()) { + MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); + if (permanent != null) { + Point source = permanent.getLocationOnScreen(); + source.translate(-parentPoint.x, -parentPoint.y); + ArrowBuilder.getBuilder().addArrow(data.gameId, (int) source.getX() + 40, (int) source.getY() + 10, (int) me.getX() + 35, (int) me.getY() + 20, Color.blue, ArrowBuilder.Type.SOURCE); + } + } + } + } + + public static void drawArrowsForTargets(TransferData data, Point parentPoint) { + java.util.List targets = data.card.getTargets(); + if (targets == null) { + return; + } + + Point me = new Point(data.locationOnScreen); + me.translate(-parentPoint.x, -parentPoint.y); + for (UUID uuid : targets) { + + PlayAreaPanel p = MageFrame.getGame(data.gameId).getPlayers().get(uuid); + if (p != null) { + Point target = p.getLocationOnScreen(); + target.translate(-parentPoint.x, -parentPoint.y); + ArrowBuilder.getBuilder().addArrow(data.gameId, (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() - 40, Color.red, ArrowBuilder.Type.TARGET); + continue; + } + + for (PlayAreaPanel panel : MageFrame.getGame(data.gameId).getPlayers().values()) { + MagePermanent permanent = panel.getBattlefieldPanel().getPermanents().get(uuid); + if (permanent != null) { + Point target = permanent.getLocationOnScreen(); + target.translate(-parentPoint.x, -parentPoint.y); + ArrowBuilder.getBuilder().addArrow(data.gameId, (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() + 10, Color.red, ArrowBuilder.Type.TARGET); + continue; + } + + PlayerView view = panel.getPlayerPanel().getPlayer(); + if (view != null) { + SimpleCardsView graveyard = view.getGraveyard(); + if (graveyard.containsKey(uuid)) { + p = MageFrame.getGame(data.gameId).getPlayers().get(view.getPlayerId()); + if (p != null) { + Point target = p.getLocationOnScreen(); + target.translate(-parentPoint.x, -parentPoint.y); + int yOffset = p.isSmallMode() ? (PlayAreaPanel.PANEL_HEIGHT - PlayAreaPanel.PANEL_HEIGHT_SMALL) : 0; + ArrowBuilder.getBuilder().addArrow(data.gameId, (int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 15, (int) target.getY() + 145 - yOffset, Color.red, ArrowBuilder.Type.TARGET); + } + } + } + } + } + } + +} diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java index 7d145f27d4e..64885273866 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java @@ -106,6 +106,8 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti private boolean displayTitleAnyway; + private JPanel cardArea; + public CardPanel(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension) { this.gameCard = newGameCard; this.callback = callback; @@ -152,7 +154,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti } if (this.gameCard.isToken()) { - // token icon + // token icon iconPanel = new JPanel(); iconPanel.setLayout(null); iconPanel.setOpaque(false); @@ -354,6 +356,11 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti repaint(); } + @Override + public void setCardAreaRef(JPanel cardArea) { + this.cardArea = cardArea; + } + public boolean getSelected() { return this.isSelected; } @@ -516,22 +523,37 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti } @Override - public final void setCardBounds(int x, int y, int width, int height) { - cardWidth = width; - cardHeight = height; - int rotCenterX = Math.round(width / 2f); - int rotCenterY = height - rotCenterX; - int rotCenterToTopCorner = Math.round(width * CardPanel.ROT_CENTER_TO_TOP_CORNER); - int rotCenterToBottomCorner = Math.round(width * CardPanel.ROT_CENTER_TO_BOTTOM_CORNER); - int xOffset = rotCenterX - rotCenterToBottomCorner; - int yOffset = rotCenterY - rotCenterToTopCorner; + public final void setCardBounds(int x, int y, int cardWidth, int cardHeight) { + this.cardWidth = cardWidth; + this.cardHeight = cardHeight; + int rotCenterX = Math.round(cardWidth / 2f); + int rotCenterY = cardHeight - rotCenterX; + int rotCenterToTopCorner = Math.round(cardWidth * CardPanel.ROT_CENTER_TO_TOP_CORNER); + int rotCenterToBottomCorner = Math.round(cardWidth * CardPanel.ROT_CENTER_TO_BOTTOM_CORNER); + int xOffset = getXOffset(cardWidth); + int yOffset = getYOffset(cardWidth, cardHeight); cardXOffset = -xOffset; cardYOffset = -yOffset; - width = -xOffset + rotCenterX + rotCenterToTopCorner; - height = -yOffset + rotCenterY + rotCenterToBottomCorner; + int width = -xOffset + rotCenterX + rotCenterToTopCorner; + int height = -yOffset + rotCenterY + rotCenterToBottomCorner; setBounds(x + xOffset, y + yOffset, width, height); } + public int getXOffset(int cardWidth) { + int rotCenterX = Math.round(cardWidth / 2f); + int rotCenterToBottomCorner = Math.round(cardWidth * CardPanel.ROT_CENTER_TO_BOTTOM_CORNER); + int xOffset = rotCenterX - rotCenterToBottomCorner; + return xOffset; + } + + public int getYOffset(int cardWidth, int cardHeight) { + int rotCenterX = Math.round(cardWidth / 2f); + int rotCenterY = cardHeight - rotCenterX; + int rotCenterToTopCorner = Math.round(cardWidth * CardPanel.ROT_CENTER_TO_TOP_CORNER); + int yOffset = rotCenterY - rotCenterToTopCorner; + return yOffset; + } + public int getCardX() { return getX() + cardXOffset; } @@ -777,6 +799,8 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti @Override public void mouseDragged(MouseEvent e) { + data.component = this; + callback.mouseDragged(e, data); } @Override @@ -834,11 +858,6 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti data.gameId = this.gameId; data.popupOffsetX = isTapped() ? cardHeight + cardXOffset + POPUP_X_GAP : cardWidth + cardXOffset + POPUP_X_GAP; data.popupOffsetY = 40; - if (this.isShowing()) { - data.locationOnScreen = this.getLocationOnScreen(); - } else { - - } return data; } @@ -965,4 +984,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti callback.mouseWheelMoved(e, data); } + public JPanel getCardArea() { + return cardArea; + } } diff --git a/Mage.Common/src/mage/cards/MageCard.java b/Mage.Common/src/mage/cards/MageCard.java index 9712a30f53a..5d72b153813 100644 --- a/Mage.Common/src/mage/cards/MageCard.java +++ b/Mage.Common/src/mage/cards/MageCard.java @@ -1,11 +1,12 @@ package mage.cards; -import java.awt.Image; -import java.util.UUID; -import javax.swing.JPanel; import mage.cards.action.ActionCallback; import mage.view.CardView; +import javax.swing.*; +import java.awt.*; +import java.util.UUID; + public abstract class MageCard extends JPanel { private static final long serialVersionUID = 6089945326434301879L; @@ -27,4 +28,5 @@ public abstract class MageCard extends JPanel { public abstract boolean isTransformed(); public abstract void showCardTitle(); public abstract void setSelected(boolean selected); + public abstract void setCardAreaRef(JPanel cardArea); } diff --git a/Mage.Common/src/mage/cards/action/ActionCallback.java b/Mage.Common/src/mage/cards/action/ActionCallback.java index 0e412fbedaa..c355ff4faeb 100644 --- a/Mage.Common/src/mage/cards/action/ActionCallback.java +++ b/Mage.Common/src/mage/cards/action/ActionCallback.java @@ -8,6 +8,7 @@ public interface ActionCallback { void mousePressed(MouseEvent e, TransferData data); void mouseReleased(MouseEvent e, TransferData data); void mouseMoved(MouseEvent e, TransferData data); + void mouseDragged(MouseEvent e, TransferData data); void mouseEntered(MouseEvent e, TransferData data); void mouseExited(MouseEvent e, TransferData data); void mouseWheelMoved(MouseWheelEvent e, TransferData data); diff --git a/Mage.Common/src/mage/cards/action/impl/EmptyCallback.java b/Mage.Common/src/mage/cards/action/impl/EmptyCallback.java index b20d7e098d7..425225a573a 100644 --- a/Mage.Common/src/mage/cards/action/impl/EmptyCallback.java +++ b/Mage.Common/src/mage/cards/action/impl/EmptyCallback.java @@ -17,6 +17,11 @@ public class EmptyCallback implements ActionCallback { public void mouseMoved(MouseEvent e, TransferData data) { } + @Override + public void mouseDragged(MouseEvent e, TransferData data) { + + } + @Override public void mouseEntered(MouseEvent e, TransferData data) { } From 045a81e66a8b744e2af2dfe4421f24a816d7ec02 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Fri, 11 Jul 2014 13:14:44 +0400 Subject: [PATCH 02/20] Fixed Issue#396: Display of stacking auras --- .../layout/impl/OldCardLayoutStrategy.java | 17 +++++++++++++++- .../org/mage/plugins/card/CardPluginImpl.java | 20 ++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java b/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java index 3b341e2832e..09dcc6ccfc6 100644 --- a/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java +++ b/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java @@ -20,6 +20,17 @@ import java.util.UUID; */ public class OldCardLayoutStrategy implements CardLayoutStrategy { + /** + * This offset is used once to shift all attachments + */ + private static final int ATTACHMENTS_DX_OFFSET = 8; + + /** + * This offset is used for each attachment + */ + private static final int ATTACHMENT_DX_OFFSET = 0; + private static final int ATTACHMENT_DY_OFFSET = 10; + @Override public void doLayout(JLayeredPane jLayeredPane, int width) { Map permanents = ((BattlefieldPanel)jLayeredPane).getPermanents(); @@ -60,7 +71,11 @@ public class OldCardLayoutStrategy implements CardLayoutStrategy { if (link != null) { link.setBounds(r); perm.getLinks().add(link); - r.translate(8, 10); + if (index == 1) { + r.translate(ATTACHMENTS_DX_OFFSET, ATTACHMENT_DY_OFFSET); // do it once + } else { + r.translate(ATTACHMENT_DX_OFFSET, ATTACHMENT_DY_OFFSET); + } perm.setBounds(r); jLayeredPane.moveToFront(link); jLayeredPane.moveToFront(perm); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java index ec2840bd185..315610c6142 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java @@ -47,6 +47,8 @@ public class CardPluginImpl implements CardPlugin { private static final Logger log = Logger.getLogger(CardPluginImpl.class); + private static final int ATTACHMENT_DY_OFFSET = 10; + private static final int GUTTER_Y = 15; private static final int GUTTER_X = 5; static final float EXTRA_CARD_SPACING_X = 0.04f; @@ -373,6 +375,9 @@ public class CardPluginImpl implements CardPlugin { } Stack stack = new Stack(); stack.add(panel); + if (panel.getOriginalPermanent().getAttachments() != null) { + stack.setMaxAttachedCount(panel.getOriginalPermanent().getAttachments().size()); + } add(stack); } } @@ -410,6 +415,11 @@ public class CardPluginImpl implements CardPlugin { private class Stack extends ArrayList { private static final long serialVersionUID = 1L; + /** + * Max attached object count attached to single permanent in the stack. + */ + private int maxAttachedCount = 0; + public Stack() { super(8); } @@ -419,7 +429,15 @@ public class CardPluginImpl implements CardPlugin { } private int getHeight() { - return cardHeight + (size() - 1) * stackSpacingY + cardSpacingY; + return cardHeight + (size() - 1) * stackSpacingY + cardSpacingY + ATTACHMENT_DY_OFFSET*maxAttachedCount; + } + + public int getMaxAttachedCount() { + return maxAttachedCount; + } + + public void setMaxAttachedCount(int maxAttachedCount) { + this.maxAttachedCount = maxAttachedCount; } } From 2b76bbf2c62edc1ee8474146ccde593f91a13a0c Mon Sep 17 00:00:00 2001 From: magenoxx Date: Fri, 11 Jul 2014 14:40:37 +0400 Subject: [PATCH 03/20] Fixed Issue#438: Enchanted permanent can't be clicked because out of sight --- .../org/mage/plugins/card/CardPluginImpl.java | 17 ++++++++++++++++- Mage.Common/src/mage/view/PermanentView.java | 19 ++++++++++++++----- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java index 315610c6142..ad6ac0bffef 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java @@ -157,6 +157,7 @@ public class CardPluginImpl implements CardPlugin { Row allCreatures = new Row(permanents, RowType.creature); Row allOthers = new Row(permanents, RowType.other); + Row allAttached = new Row(permanents, RowType.attached); boolean othersOnTheRight = true; if (options != null && options.containsKey("nonLandPermanentsInOnePile")) { @@ -268,6 +269,14 @@ public class CardPluginImpl implements CardPlugin { y = rowBottom; } + // we need this only for defining card size + // attached permanents will be handled separately + for (Stack stack : allAttached) { + for (MagePermanent panel : stack) { + panel.setCardBounds(0, 0, cardWidth, cardHeight); + } + } + return y; } @@ -340,7 +349,7 @@ public class CardPluginImpl implements CardPlugin { } private static enum RowType { - land, creature, other; + land, creature, other, attached; public boolean isType(MagePermanent card) { switch (this) { @@ -350,6 +359,8 @@ public class CardPluginImpl implements CardPlugin { return CardUtil.isCreature(card); case other: return !CardUtil.isLand(card) && !CardUtil.isCreature(card); + case attached: + return card.getOriginalPermanent().isAttachedTo(); default: throw new RuntimeException("Unhandled type: " + this); } @@ -373,6 +384,10 @@ public class CardPluginImpl implements CardPlugin { if (!type.isType(panel)) { continue; } + // all attached permanents are grouped separately later + if (!type.equals(RowType.attached) && RowType.attached.isType(panel)) { + continue; + } Stack stack = new Stack(); stack.add(panel); if (panel.getOriginalPermanent().getAttachments() != null) { diff --git a/Mage.Common/src/mage/view/PermanentView.java b/Mage.Common/src/mage/view/PermanentView.java index f9e4efd2a26..ea7dba67855 100644 --- a/Mage.Common/src/mage/view/PermanentView.java +++ b/Mage.Common/src/mage/view/PermanentView.java @@ -28,9 +28,6 @@ package mage.view; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.TurnFaceUpAbility; import mage.abilities.common.TurnedFaceUpTriggeredAbility; @@ -38,10 +35,13 @@ import mage.cards.Card; import mage.constants.Rarity; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentToken; import mage.players.Player; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** * * @author BetaSteward_at_googlemail.com @@ -59,6 +59,7 @@ public class PermanentView extends CardView { private final boolean copy; private final String nameOwner; // only filled if != controller private final boolean controlled; + private UUID attachedTo; public PermanentView(Permanent permanent, Card card, UUID createdForPlayerId, Game game) { super(permanent, null, permanent.getControllerId().equals(createdForPlayerId)); @@ -73,6 +74,7 @@ public class PermanentView extends CardView { attachments = new ArrayList<>(); attachments.addAll(permanent.getAttachments()); } + this.attachedTo = permanent.getAttachedTo(); if (isToken()) { original = new CardView(((PermanentToken)permanent).getToken()); original.expansionSetCode = permanent.getExpansionSetCode(); @@ -179,5 +181,12 @@ public class PermanentView extends CardView { public boolean isControlled() { return controlled; } - + + public UUID getAttachedTo() { + return attachedTo; + } + + public boolean isAttachedTo() { + return attachedTo != null; + } } From 1551c111f2b81a1c8bf4637f5e51a9461b4eaaf7 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 11 Jul 2014 15:26:00 +0200 Subject: [PATCH 04/20] Added Eldamiri's Call. --- .../mage/sets/planeshift/EladamrisCall.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/planeshift/EladamrisCall.java diff --git a/Mage.Sets/src/mage/sets/planeshift/EladamrisCall.java b/Mage.Sets/src/mage/sets/planeshift/EladamrisCall.java new file mode 100644 index 00000000000..02770df1c8e --- /dev/null +++ b/Mage.Sets/src/mage/sets/planeshift/EladamrisCall.java @@ -0,0 +1,70 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.planeshift; + +import java.util.UUID; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author LevelX2 + */ +public class EladamrisCall extends CardImpl { + + private static final FilterCard filter = new FilterCard("creature card"); + + static { + filter.add(new CardTypePredicate(CardType.CREATURE)); + } + + public EladamrisCall(UUID ownerId) { + super(ownerId, 106, "Eladamri's Call", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{G}{W}"); + this.expansionSetCode = "PLS"; + + this.color.setGreen(true); + this.color.setWhite(true); + + // Search your library for a creature card, reveal that card, and put it into your hand. Then shuffle your library. + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true)); + } + + public EladamrisCall(final EladamrisCall card) { + super(card); + } + + @Override + public EladamrisCall copy() { + return new EladamrisCall(this); + } +} From 2fcf6559945d5e1bf6d27d17cc86fe99f14a69f6 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 11 Jul 2014 15:26:36 +0200 Subject: [PATCH 05/20] [M15] Changes to tooltip text. --- Mage.Sets/src/mage/sets/magic2015/AvariceAmulet.java | 9 +++++---- Mage.Sets/src/mage/sets/magic2015/BroodKeeper.java | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2015/AvariceAmulet.java b/Mage.Sets/src/mage/sets/magic2015/AvariceAmulet.java index 7068a06521d..7a53f42368a 100644 --- a/Mage.Sets/src/mage/sets/magic2015/AvariceAmulet.java +++ b/Mage.Sets/src/mage/sets/magic2015/AvariceAmulet.java @@ -68,18 +68,19 @@ public class AvariceAmulet extends CardImpl { // Equipped creature gets +2/+0 Effect effect = new BoostEquippedEffect(2, 0); effect.setText("Equipped creature gets +2/+0"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); // and has vigilance effect = new GainAbilityAttachedEffect(VigilanceAbility.getInstance(), AttachmentType.EQUIPMENT); effect.setText("and has vigilance"); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + ability.addEffect(effect); //and "At the beginning of your upkeep, draw a card." effect = new GainAbilityAttachedEffect(new BeginningOfUpkeepTriggeredAbility(new DrawCardSourceControllerEffect(1), TargetController.YOU, false), AttachmentType.EQUIPMENT); effect.setText("and \"At the beginning of your upkeep, draw a card.\""); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + ability.addEffect(effect); + this.addAbility(ability); // When equipped creature dies, target opponent gains control of Avarice Amulet. - Ability ability = new DiesAttachedTriggeredAbility(new AvariceAmuletChangeControlEffect(), "equipped creature", false); + ability = new DiesAttachedTriggeredAbility(new AvariceAmuletChangeControlEffect(), "equipped creature", false); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/magic2015/BroodKeeper.java b/Mage.Sets/src/mage/sets/magic2015/BroodKeeper.java index 409b4061b40..6cc30b63679 100644 --- a/Mage.Sets/src/mage/sets/magic2015/BroodKeeper.java +++ b/Mage.Sets/src/mage/sets/magic2015/BroodKeeper.java @@ -32,6 +32,7 @@ import mage.MageInt; import mage.abilities.common.AuraAttachedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continious.BoostSourceEffect; import mage.abilities.keyword.FlyingAbility; @@ -60,7 +61,9 @@ public class BroodKeeper extends CardImpl { // Whenever an Aura becomes attached to Brood Keeper, put a 2/2 red Dragon creature token with flying onto the battlefield. // It has "{R}: This creature gets +1/+0 until end of turn." - this.addAbility(new AuraAttachedTriggeredAbility(new CreateTokenEffect(new BroodKeeperDragonToken()), false)); + Effect effect = new CreateTokenEffect(new BroodKeeperDragonToken()); + effect.setText("put a 2/2 red Dragon creature token with flying onto the battlefield. It has \"{R}: This creature gets +1/+0 until end of turn.\""); + this.addAbility(new AuraAttachedTriggeredAbility(effect, false)); } public BroodKeeper(final BroodKeeper card) { From 5fcc3b1ba89382fe614d75a92f9da1bf168098a9 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Fri, 11 Jul 2014 19:35:01 +0400 Subject: [PATCH 06/20] [UI] Displaying playable cards in hand --- .../main/java/mage/client/cards/Cards.java | 2 +- .../main/java/mage/client/game/GamePanel.java | 89 +++++-------------- .../java/org/mage/card/arcane/CardPanel.java | 8 ++ Mage.Common/src/mage/view/CardView.java | 22 +++-- Mage.Common/src/mage/view/GameView.java | 16 ++-- .../java/mage/server/game/GameSession.java | 46 +++++----- Mage/src/mage/game/GameImpl.java | 47 ++-------- Mage/src/mage/players/Player.java | 19 ++-- Mage/src/mage/players/PlayerImpl.java | 63 ++++++------- 9 files changed, 119 insertions(+), 193 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/cards/Cards.java b/Mage.Client/src/main/java/mage/client/cards/Cards.java index 8387066fe39..831a53e0c14 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Cards.java +++ b/Mage.Client/src/main/java/mage/client/cards/Cards.java @@ -310,7 +310,7 @@ public class Cards extends javax.swing.JPanel { Collections.sort(cards, new Comparator() { @Override public int compare(CardPanel cp1, CardPanel cp2) { - return Integer.valueOf(cp1.getLocation().x).compareTo(Integer.valueOf(cp2.getLocation().x)); + return Integer.valueOf(cp1.getLocation().x).compareTo(cp2.getLocation().x); } }); 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 f2358331cb3..e4e31cdd707 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -27,47 +27,6 @@ */ package mage.client.game; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.ComponentListener; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import javax.swing.AbstractAction; -import javax.swing.GroupLayout; -import javax.swing.GroupLayout.Alignment; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JLayeredPane; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.KeyStroke; -import javax.swing.SwingWorker; -import javax.swing.border.LineBorder; -import javax.swing.plaf.basic.BasicSplitPaneDivider; -import javax.swing.plaf.basic.BasicSplitPaneUI; import mage.cards.Card; import mage.cards.action.ActionCallback; import mage.client.MageFrame; @@ -78,12 +37,7 @@ import mage.client.components.HoverButton; import mage.client.components.MageComponents; import mage.client.components.ext.dlg.DialogManager; import mage.client.components.layout.RelativeLayout; -import mage.client.dialog.ExileZoneDialog; -import mage.client.dialog.PickChoiceDialog; -import mage.client.dialog.PickNumberDialog; -import mage.client.dialog.PickPileDialog; -import mage.client.dialog.PreferencesDialog; -import mage.client.dialog.ShowCardsDialog; +import mage.client.dialog.*; import mage.client.game.FeedbackPanel.FeedbackMode; import mage.client.plugins.adapters.MageActionCallback; import mage.client.plugins.impl.Plugins; @@ -94,29 +48,23 @@ import mage.client.util.PhaseManager; import mage.client.util.gui.ArrowBuilder; import mage.constants.EnlargeMode; import mage.constants.PhaseStep; -import static mage.constants.PhaseStep.BEGIN_COMBAT; -import static mage.constants.PhaseStep.COMBAT_DAMAGE; -import static mage.constants.PhaseStep.DECLARE_ATTACKERS; -import static mage.constants.PhaseStep.DECLARE_BLOCKERS; -import static mage.constants.PhaseStep.DRAW; -import static mage.constants.PhaseStep.END_COMBAT; -import static mage.constants.PhaseStep.END_TURN; -import static mage.constants.PhaseStep.FIRST_COMBAT_DAMAGE; -import static mage.constants.PhaseStep.UNTAP; -import static mage.constants.PhaseStep.UPKEEP; import mage.remote.Session; -import mage.view.AbilityPickerView; -import mage.view.CardsView; -import mage.view.ExileView; -import mage.view.GameView; -import mage.view.LookedAtView; -import mage.view.MatchView; -import mage.view.PlayerView; -import mage.view.RevealedView; -import mage.view.SimpleCardsView; +import mage.view.*; import org.apache.log4j.Logger; import org.mage.plugins.card.utils.impl.ImageManagerImpl; +import javax.swing.*; +import javax.swing.GroupLayout.Alignment; +import javax.swing.border.LineBorder; +import javax.swing.plaf.basic.BasicSplitPaneDivider; +import javax.swing.plaf.basic.BasicSplitPaneUI; +import java.awt.*; +import java.awt.event.*; +import java.io.Serializable; +import java.util.*; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; + /** * * @author BetaSteward_at_googlemail.com, nantuko8 @@ -503,6 +451,15 @@ public final class GamePanel extends javax.swing.JPanel { handCards.clear(); handCards.put(YOUR_HAND, CardsViewUtil.convertSimple(game.getHand(), loadedCards)); + // Mark playable + if (game.getCanPlayInHand() != null) { + for (CardView card : handCards.get(YOUR_HAND).values()) { + if (game.getCanPlayInHand().contains(card.getId())) { + card.setPlayable(true); + } + } + } + // Get opponents hand cards if available if (game.getOpponentHands() != null) { for (Map.Entry hand: game.getOpponentHands().entrySet()) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java index 64885273866..2ac7f7f54b2 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java @@ -80,6 +80,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti public int cardXOffset, cardYOffset, cardWidth, cardHeight; private boolean isSelected; + private boolean isPlayable; private boolean showCastingCost; private boolean hasImage = false; private float alpha = 1.0f; @@ -420,6 +421,11 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); } + if (isPlayable) { + g2d.setColor(new Color(250, 250, 0, 200)); + g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); + } + //TODO:uncomment /* if (gameCard.isAttacking()) { @@ -708,6 +714,8 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti } setText(card); + this.isPlayable = card.isPlayable(); + boolean updateImage = !gameCard.getName().equals(card.getName()) || gameCard.isFaceDown() != card.isFaceDown(); // update after e.g. turning a night/day card this.gameCard = card; diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index a555a2ce6a9..513bfe14a13 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -28,17 +28,11 @@ package mage.view; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageObject; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.Modes; import mage.abilities.SpellAbility; -import mage.abilities.common.TurnFaceUpAbility; import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.keyword.MorphAbility; import mage.cards.Card; import mage.cards.SplitCard; import mage.constants.CardType; @@ -49,7 +43,6 @@ import mage.counters.Counter; import mage.counters.CounterType; import mage.game.command.Emblem; import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentToken; import mage.game.permanent.token.Token; import mage.game.stack.Spell; @@ -57,6 +50,10 @@ import mage.game.stack.StackAbility; import mage.target.Target; import mage.target.Targets; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** * @author BetaSteward_at_googlemail.com */ @@ -114,6 +111,8 @@ public class CardView extends SimpleCardView { protected boolean rotate; protected boolean hideInfo; // controlls if the tooltip window is shown (eg. controlled face down morph card) + protected boolean isPlayable; + public CardView(Card card) { this(card, null, false); } @@ -673,5 +672,12 @@ public class CardView extends SimpleCardView { public boolean hideInfo() { return hideInfo; } - + + public boolean isPlayable() { + return isPlayable; + } + + public void setPlayable(boolean isPlayable) { + this.isPlayable = isPlayable; + } } diff --git a/Mage.Common/src/mage/view/GameView.java b/Mage.Common/src/mage/view/GameView.java index 8e833eb459c..bdf6779a6b6 100644 --- a/Mage.Common/src/mage/view/GameView.java +++ b/Mage.Common/src/mage/view/GameView.java @@ -28,11 +28,6 @@ package mage.view; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; import mage.MageObject; import mage.abilities.costs.Cost; import mage.cards.Card; @@ -51,6 +46,9 @@ import mage.game.stack.StackAbility; import mage.game.stack.StackObject; import mage.players.Player; +import java.io.Serializable; +import java.util.*; + /** * @@ -62,6 +60,7 @@ public class GameView implements Serializable { private final int priorityTime; private final List players = new ArrayList<>(); private SimpleCardsView hand; + private Set canPlayInHand; private Map opponentHands; private final CardsView stack = new CardsView(); private final List exiles = new ArrayList<>(); @@ -280,4 +279,11 @@ public class GameView implements Serializable { return isPlayer; } + public Set getCanPlayInHand() { + return canPlayInHand; + } + + public void setCanPlayInHand(Set canPlayInHand) { + this.canPlayInHand = canPlayInHand; + } } diff --git a/Mage.Server/src/main/java/mage/server/game/GameSession.java b/Mage.Server/src/main/java/mage/server/game/GameSession.java index fd59bb8c32a..63d9f13cc42 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameSession.java +++ b/Mage.Server/src/main/java/mage/server/game/GameSession.java @@ -28,17 +28,6 @@ package mage.server.game; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import mage.cards.Cards; import mage.constants.ManaType; import mage.game.Game; @@ -50,14 +39,16 @@ import mage.server.User; import mage.server.UserManager; import mage.server.util.ConfigSettings; import mage.server.util.ThreadExecutor; -import mage.view.AbilityPickerView; -import mage.view.CardsView; -import mage.view.GameClientMessage; -import mage.view.GameView; -import mage.view.LookedAtView; -import mage.view.SimpleCardsView; +import mage.view.*; import org.apache.log4j.Logger; +import java.io.Serializable; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + /** * * @author BetaSteward_at_googlemail.com @@ -245,15 +236,9 @@ public class GameSession extends GameWatcher { player.setUserData(this.userData); GameView gameView = new GameView(game.getState(), game, playerId); gameView.setHand(new SimpleCardsView(player.getHand().getCards(game))); + gameView.setCanPlayInHand(player.getPlayableInHand(game)); - if (player.getPlayersUnderYourControl().size() > 0) { - Map handCards = new HashMap<>(); - for (UUID controlledPlayerId : player.getPlayersUnderYourControl()) { - Player opponent = game.getPlayer(controlledPlayerId); - handCards.put(opponent.getName(), new SimpleCardsView(opponent.getHand().getCards(game))); - } - gameView.setOpponentHands(handCards); - } + processControlledPlayers(player, gameView); //TODO: should player who controls another player's turn be able to look at all these cards? @@ -267,6 +252,17 @@ public class GameSession extends GameWatcher { return gameView; } + private void processControlledPlayers(Player player, GameView gameView) { + if (player.getPlayersUnderYourControl().size() > 0) { + Map handCards = new HashMap<>(); + for (UUID controlledPlayerId : player.getPlayersUnderYourControl()) { + Player opponent = game.getPlayer(controlledPlayerId); + handCards.put(opponent.getName(), new SimpleCardsView(opponent.getHand().getCards(game))); + } + gameView.setOpponentHands(handCards); + } + } + public void removeGame() { User user = UserManager.getInstance().getUser(userId); if (user != null) { diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index db09699d250..71b0dcddff2 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -28,23 +28,6 @@ package mage.game; -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; -import java.util.Stack; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; @@ -68,13 +51,7 @@ import mage.cards.CardsImpl; import mage.cards.SplitCard; import mage.cards.decks.Deck; import mage.choices.Choice; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.MultiplayerAttackOption; -import mage.constants.Outcome; -import mage.constants.PhaseStep; -import mage.constants.RangeOfInfluence; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.Filter; import mage.filter.FilterPermanent; @@ -89,14 +66,8 @@ import mage.game.combat.Combat; import mage.game.command.CommandObject; import mage.game.command.Commander; import mage.game.command.Emblem; -import mage.game.events.DamageEvent; -import mage.game.events.GameEvent; -import mage.game.events.Listener; -import mage.game.events.PlayerQueryEvent; -import mage.game.events.PlayerQueryEventSource; -import mage.game.events.TableEvent; +import mage.game.events.*; import mage.game.events.TableEvent.EventType; -import mage.game.events.TableEventSource; import mage.game.permanent.Battlefield; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; @@ -114,14 +85,14 @@ import mage.target.Target; import mage.target.TargetPermanent; import mage.target.TargetPlayer; import mage.util.functions.ApplyToPermanent; -import mage.watchers.common.CastSpellLastTurnWatcher; -import mage.watchers.common.MiracleWatcher; -import mage.watchers.common.MorbidWatcher; -import mage.watchers.common.PlayerDamagedBySourceWatcher; -import mage.watchers.common.PlayerLostLifeWatcher; -import mage.watchers.common.SoulbondWatcher; +import mage.watchers.common.*; import org.apache.log4j.Logger; +import java.io.IOException; +import java.io.Serializable; +import java.util.*; +import java.util.Map.Entry; + public abstract class GameImpl implements Game, Serializable { private static final transient Logger logger = Logger.getLogger(GameImpl.class); @@ -1846,7 +1817,7 @@ public abstract class GameImpl implements Game, Serializable { @Override public boolean canPlaySorcery(UUID playerId) { - return getActivePlayerId().equals(playerId) && getStack().isEmpty() && isMainPhase(); + return isMainPhase() && getActivePlayerId().equals(playerId) && getStack().isEmpty(); } /** diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index 2df689ddfea..c32f3406a17 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -28,21 +28,9 @@ package mage.players; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; import mage.MageItem; import mage.MageObject; -import mage.abilities.Abilities; -import mage.abilities.Ability; -import mage.abilities.ActivatedAbility; -import mage.abilities.Mode; -import mage.abilities.Modes; -import mage.abilities.SpellAbility; -import mage.abilities.TriggeredAbility; +import mage.abilities.*; import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.ManaCost; @@ -69,6 +57,9 @@ import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; import mage.util.Copyable; +import java.io.Serializable; +import java.util.*; + /** * * @author BetaSteward_at_googlemail.com @@ -340,6 +331,8 @@ public interface Player extends MageItem, Copyable { List getPlayable(Game game, boolean hidden); List getPlayableOptions(Ability ability, Game game); + Set getPlayableInHand(Game game); + void addCounters(Counter counter, Game game); List getAttachments(); boolean addAttachment(UUID permanentId, Game game); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index dfdfb02b559..f0215aaec08 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -28,31 +28,9 @@ package mage.players; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.Mana; -import mage.abilities.Abilities; -import mage.abilities.AbilitiesImpl; -import mage.abilities.Ability; -import mage.abilities.ActivatedAbility; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.Mode; -import mage.abilities.PlayLandAbility; -import mage.abilities.SpecialAction; -import mage.abilities.SpellAbility; -import mage.abilities.TriggeredAbility; +import mage.abilities.*; import mage.abilities.common.PassAbility; import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility; import mage.abilities.costs.AdjustingSourceCosts; @@ -62,12 +40,7 @@ import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.RestrictionUntapNotMoreThanEffect; import mage.abilities.effects.common.LoseControlOnOtherPlayersControllerEffect; -import mage.abilities.keyword.FlashbackAbility; -import mage.abilities.keyword.HexproofAbility; -import mage.abilities.keyword.InfectAbility; -import mage.abilities.keyword.LifelinkAbility; -import mage.abilities.keyword.ProtectionAbility; -import mage.abilities.keyword.ShroudAbility; +import mage.abilities.keyword.*; import mage.abilities.mana.ManaAbility; import mage.abilities.mana.ManaOptions; import mage.actions.MageDrawAction; @@ -76,14 +49,7 @@ import mage.cards.Cards; import mage.cards.CardsImpl; import mage.cards.SplitCard; import mage.cards.decks.Deck; -import mage.constants.AsThoughEffectType; -import mage.constants.CardType; -import mage.constants.ManaType; -import mage.constants.Outcome; -import mage.constants.RangeOfInfluence; -import mage.constants.SpellAbilityType; -import mage.constants.TimingRule; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.Counter; import mage.counters.CounterType; import mage.counters.Counters; @@ -116,6 +82,10 @@ import mage.target.common.TargetDiscard; import mage.watchers.common.BloodthirstWatcher; import org.apache.log4j.Logger; +import java.io.Serializable; +import java.util.*; +import java.util.Map.Entry; + public abstract class PlayerImpl implements Player, Serializable { private static final transient Logger log = Logger.getLogger(PlayerImpl.class); @@ -1973,6 +1943,25 @@ public abstract class PlayerImpl implements Player, Serializable { return playable; } + @Override + public Set getPlayableInHand(Game game) { + Set playable = new HashSet<>(); + + ManaOptions available = getManaAvailable(game); + available.addMana(manaPool.getMana()); + + for (Card card: hand.getCards(game)) { + for (ActivatedAbility ability: card.getAbilities().getPlayableAbilities(Zone.HAND)) { + if (canPlay(ability, available, game)) { + playable.add(card.getId()); + break; + } + } + } + + return playable; + } + /** * Only used for AIs * From bcb91dade10fc7238b12c7c41cbeacbca3055e28 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Fri, 11 Jul 2014 21:52:24 +0400 Subject: [PATCH 07/20] [UI] Fixed popup location --- Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java index 2ac7f7f54b2..6f83805a4fa 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java @@ -864,6 +864,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti data.card = this.gameCard; data.popupText = popupText; data.gameId = this.gameId; + data.locationOnScreen = data.component.getLocationOnScreen(); // we need this for popup data.popupOffsetX = isTapped() ? cardHeight + cardXOffset + POPUP_X_GAP : cardWidth + cardXOffset + POPUP_X_GAP; data.popupOffsetY = 40; return data; From 8ca8d80a5c6c6225955d835d6a7af575adf359e7 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Sat, 12 Jul 2014 11:20:01 +0400 Subject: [PATCH 08/20] [UI] Fixed Issue#454: Enchanted land position is not correct on the layout --- .../client/util/layout/impl/OldCardLayoutStrategy.java | 9 +++++---- .../main/java/org/mage/plugins/card/CardPluginImpl.java | 5 +++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java b/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java index 09dcc6ccfc6..528b702cd0a 100644 --- a/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java +++ b/Mage.Client/src/main/java/mage/client/util/layout/impl/OldCardLayoutStrategy.java @@ -33,17 +33,18 @@ public class OldCardLayoutStrategy implements CardLayoutStrategy { @Override public void doLayout(JLayeredPane jLayeredPane, int width) { - Map permanents = ((BattlefieldPanel)jLayeredPane).getPermanents(); - JLayeredPane jPanel = ((BattlefieldPanel)jLayeredPane).getMainPanel(); + Map permanents = ((BattlefieldPanel) jLayeredPane).getPermanents(); + JLayeredPane jPanel = ((BattlefieldPanel) jLayeredPane).getMainPanel(); - int height = Plugins.getInstance().sortPermanents(((BattlefieldPanel)jLayeredPane).getUiComponentsList(), permanents.values()); + int height = Plugins.getInstance().sortPermanents(((BattlefieldPanel) jLayeredPane).getUiComponentsList(), permanents.values()); jPanel.setPreferredSize(new Dimension(width - 30, height)); - for (PermanentView permanent: ((BattlefieldPanel)jLayeredPane).getBattlefield().values()) { + for (PermanentView permanent : ((BattlefieldPanel) jLayeredPane).getBattlefield().values()) { if (permanent.getAttachments() != null) { groupAttachments(jLayeredPane, jPanel, permanents, permanent); } } + } private void groupAttachments(JLayeredPane jLayeredPane, JLayeredPane jPanel, Map permanents, PermanentView permanent) { diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java index ad6ac0bffef..356c5498601 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java @@ -151,6 +151,11 @@ public class CardPluginImpl implements CardPlugin { } Stack stack = new Stack(); + + if (permanent.getOriginalPermanent().getAttachments() != null) { + stack.setMaxAttachedCount(permanent.getOriginalPermanent().getAttachments().size()); + } + stack.add(permanent); allLands.add(insertIndex == -1 ? allLands.size() : insertIndex, stack); } From fb30f685de21092611c5112b1e436aefed7db3d3 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Jul 2014 14:32:10 +0200 Subject: [PATCH 09/20] * Deftblade Elite - Fixed wrong activation cost. --- Mage.Sets/src/mage/sets/vintagemasters/DeftbladeElite.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/vintagemasters/DeftbladeElite.java b/Mage.Sets/src/mage/sets/vintagemasters/DeftbladeElite.java index 9145a4edc9c..b2bf080f18f 100644 --- a/Mage.Sets/src/mage/sets/vintagemasters/DeftbladeElite.java +++ b/Mage.Sets/src/mage/sets/vintagemasters/DeftbladeElite.java @@ -63,7 +63,7 @@ public class DeftbladeElite extends CardImpl { // {1}{W}: Prevent all combat damage that would be dealt to and dealt by Deftblade Elite this turn. Effect effect = new PreventCombatDamageToSourceEffect(Duration.EndOfTurn); effect.setText("Prevent all combat damage that would be dealt to"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{3}{W}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{1}{W}")); effect = new PreventCombatDamageBySourceEffect(Duration.EndOfTurn); effect.setText("and dealt by {this} this turn"); ability.addEffect(effect); From 1602b5ae061de6e6a42bf418f63ce7edc787d6a6 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Jul 2014 14:38:00 +0200 Subject: [PATCH 10/20] * Cephalid Coliseum - Fixed wrong Threshold cost. --- Mage.Sets/src/mage/sets/odyssey/CephalidColiseum.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/odyssey/CephalidColiseum.java b/Mage.Sets/src/mage/sets/odyssey/CephalidColiseum.java index a725b3a0493..f2bed0f7c83 100644 --- a/Mage.Sets/src/mage/sets/odyssey/CephalidColiseum.java +++ b/Mage.Sets/src/mage/sets/odyssey/CephalidColiseum.java @@ -62,9 +62,9 @@ public class CephalidColiseum extends CardImpl { // Threshold - {U}, {tap}, Sacrifice Cephalid Coliseum: Target player draws three cards, then discards three cards. Activate this ability only if seven or more cards are in your graveyard. Ability thresholdAbility = new ConditionalGainActivatedAbility(Zone.BATTLEFIELD, new DrawCardTargetEffect(3), - new ManaCostsImpl("{G}"), + new ManaCostsImpl("{U}"), new CardsInControllerGraveCondition(7), - "Threshold - {G}, {T}, Sacrifice {this}: Target player draws three cards, then discards three cards. Activate this ability only if seven or more cards are in your graveyard."); + "Threshold - {U}, {T}, Sacrifice {this}: Target player draws three cards, then discards three cards. Activate this ability only if seven or more cards are in your graveyard."); thresholdAbility.addEffect(new DiscardTargetEffect(3)); thresholdAbility.addCost(new TapSourceCost()); thresholdAbility.addCost(new SacrificeSourceCost()); From 3b7991a5f0be29f1a8b40947f32ee96cbeb3659b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Jul 2014 14:45:37 +0200 Subject: [PATCH 11/20] * Astral Slide - Fixed that the exiled creature did not return at next end step. --- Mage.Sets/src/mage/sets/onslaught/AstralSlide.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/onslaught/AstralSlide.java b/Mage.Sets/src/mage/sets/onslaught/AstralSlide.java index bea92b7d387..b18643abe50 100644 --- a/Mage.Sets/src/mage/sets/onslaught/AstralSlide.java +++ b/Mage.Sets/src/mage/sets/onslaught/AstralSlide.java @@ -28,6 +28,7 @@ package mage.sets.onslaught; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.CycleAllTriggeredAbility; import mage.abilities.common.delayed.AtEndOfTurnDelayedTriggeredAbility; @@ -86,12 +87,14 @@ class AstralSlideEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null) { Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { - if (controller.moveCardToExileWithInfo(permanent, null, "", source.getSourceId(), game, Zone.BATTLEFIELD)) { + UUID exileId = UUID.randomUUID(); + if (controller.moveCardToExileWithInfo(permanent, exileId, sourceObject.getLogName(), source.getSourceId(), game, Zone.BATTLEFIELD)) { //create delayed triggered ability - AtEndOfTurnDelayedTriggeredAbility delayedAbility = new AtEndOfTurnDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD, false)); + AtEndOfTurnDelayedTriggeredAbility delayedAbility = new AtEndOfTurnDelayedTriggeredAbility(new ReturnFromExileEffect(exileId, Zone.BATTLEFIELD, false)); delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setControllerId(source.getControllerId()); game.addDelayedTriggeredAbility(delayedAbility); From 6b582109a2fd5c4e29799b77acd98df21b7a1343 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Jul 2014 15:09:32 +0200 Subject: [PATCH 12/20] * Tangle Wire - Fixed that the tap effect was handled wrongly targeted. --- Mage.Sets/src/mage/sets/nemesis/TangleWire.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/nemesis/TangleWire.java b/Mage.Sets/src/mage/sets/nemesis/TangleWire.java index a335510424b..4279da7ee56 100644 --- a/Mage.Sets/src/mage/sets/nemesis/TangleWire.java +++ b/Mage.Sets/src/mage/sets/nemesis/TangleWire.java @@ -106,7 +106,7 @@ class TangleWireEffect extends OneShotEffect { int counterCount = permanent.getCounters().getCount(CounterType.FADE); int amount = Math.min(counterCount, targetCount); - Target target = new TargetControlledPermanent(amount, amount, filter, false); + Target target = new TargetControlledPermanent(amount, amount, filter, true); target.setNotTarget(true); if (amount > 0 && player.chooseTarget(Outcome.Tap, target, source, game)) { From 59356254f5d07d8f6a992b1a76e571656d854d67 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Jul 2014 15:20:02 +0200 Subject: [PATCH 13/20] * Skirk Prospector - Fixed wrong cost of the activated ability. --- .../src/mage/sets/onslaught/SkirkProspector.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/onslaught/SkirkProspector.java b/Mage.Sets/src/mage/sets/onslaught/SkirkProspector.java index 56c9181c018..76cc122e088 100644 --- a/Mage.Sets/src/mage/sets/onslaught/SkirkProspector.java +++ b/Mage.Sets/src/mage/sets/onslaught/SkirkProspector.java @@ -34,9 +34,13 @@ import mage.constants.Rarity; import mage.MageInt; import mage.Mana; import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.target.common.TargetControlledCreaturePermanent; /** * @@ -44,6 +48,12 @@ import mage.constants.Zone; */ public class SkirkProspector extends CardImpl { + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a Goblin"); + + static { + filter.add(new SubtypePredicate(("Goblin"))); + } + public SkirkProspector(UUID ownerId) { super(ownerId, 230, "Skirk Prospector", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{R}"); this.expansionSetCode = "ONS"; @@ -54,7 +64,7 @@ public class SkirkProspector extends CardImpl { this.toughness = new MageInt(1); // Sacrifice a Goblin: Add {R} to your mana pool. - this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.RedMana, new SacrificeSourceCost())); + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.RedMana, new SacrificeTargetCost(new TargetControlledCreaturePermanent(1,1,filter,true)))); } public SkirkProspector(final SkirkProspector card) { From ff011f8ab82d8b3b65036c359f1f69a5d45af92c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Jul 2014 19:30:00 +0200 Subject: [PATCH 14/20] * Belligerent Sliver - Fixed that other slivers got the ability as own ability. --- Mage.Sets/src/mage/sets/magic2015/BelligerentSliver.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2015/BelligerentSliver.java b/Mage.Sets/src/mage/sets/magic2015/BelligerentSliver.java index 750752aff26..bf17e54f41a 100644 --- a/Mage.Sets/src/mage/sets/magic2015/BelligerentSliver.java +++ b/Mage.Sets/src/mage/sets/magic2015/BelligerentSliver.java @@ -29,9 +29,11 @@ package mage.sets.magic2015; import java.util.UUID; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.combat.CantBeBlockedByOneAllEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByOneEffect; +import mage.abilities.effects.common.continious.GainAbilityAllEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; @@ -65,7 +67,8 @@ public class BelligerentSliver extends CardImpl { this.toughness = new MageInt(2); // Sliver creatures you control have "This creature can't be blocked except by two or more creatures." - Effect effect = new CantBeBlockedByOneAllEffect(2, filter, Duration.WhileOnBattlefield); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeBlockedByOneEffect(2, Duration.WhileOnBattlefield)); + Effect effect = new GainAbilityAllEffect(ability, Duration.WhileOnBattlefield, filter); effect.setText("Sliver creatures you control have \"This creature can't be blocked except by two or more creatures.\""); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } From b602be2b53cb08a921e31d0e5e9dd906787bde7c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Jul 2014 19:31:57 +0200 Subject: [PATCH 15/20] * Fixed that spells with target amount distribution did not fizzle if all their targets were illegal at resolution (e.g. Electrolyze). --- Mage/src/mage/players/PlayerImpl.java | 2 +- Mage/src/mage/target/TargetImpl.java | 2 +- .../src/mage/target/common/TargetCreatureOrPlayerAmount.java | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index f0215aaec08..df3a955396b 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -1514,7 +1514,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void idleTimeout(Game game) { - game.informPlayers(new StringBuilder(getName()).append(" has run out of time. Loosing the Match.").toString()); + game.informPlayers(new StringBuilder(getName()).append(" was idle for too long. Loosing the Match.").toString()); quit = true; idleTimeout = true; this.concede(game); diff --git a/Mage/src/mage/target/TargetImpl.java b/Mage/src/mage/target/TargetImpl.java index d0a07104589..0fa8de6a45d 100644 --- a/Mage/src/mage/target/TargetImpl.java +++ b/Mage/src/mage/target/TargetImpl.java @@ -327,7 +327,7 @@ public abstract class TargetImpl implements Target { @Override public boolean isLegal(Ability source, Game game) { //20101001 - 608.2b - Set illegalTargets = new HashSet(); + Set illegalTargets = new HashSet<>(); int replacedTargets = 0; for (UUID targetId: targets.keySet()) { Card card = game.getCard(targetId); diff --git a/Mage/src/mage/target/common/TargetCreatureOrPlayerAmount.java b/Mage/src/mage/target/common/TargetCreatureOrPlayerAmount.java index e639224aba5..d1a3d47eb4a 100644 --- a/Mage/src/mage/target/common/TargetCreatureOrPlayerAmount.java +++ b/Mage/src/mage/target/common/TargetCreatureOrPlayerAmount.java @@ -53,7 +53,12 @@ public class TargetCreatureOrPlayerAmount extends TargetAmount { protected FilterCreatureOrPlayer filter; public TargetCreatureOrPlayerAmount(int amount) { + // 107.1c If a rule or ability instructs a player to choose “any number,” that player may choose + // any positive number or zero, unless something (such as damage or counters) is being divided + // or distributed among “any number” of players and/or objects. In that case, a nonzero number + // of players and/or objects must be chosen if possible. this(new StaticValue(amount)); + this.minNumberOfTargets = 1; } public TargetCreatureOrPlayerAmount(DynamicValue amount) { From 25b96900c52a69c8a000901172b10a3bf7a0746c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Jul 2014 23:53:15 +0200 Subject: [PATCH 16/20] * Athreos, God of Passage - Fixed that the triggered ability did not work. --- .../sets/journeyintonyx/AthreosGodOfPassage.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/AthreosGodOfPassage.java b/Mage.Sets/src/mage/sets/journeyintonyx/AthreosGodOfPassage.java index ca752e3fb3a..b847dd73d7f 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/AthreosGodOfPassage.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/AthreosGodOfPassage.java @@ -87,7 +87,7 @@ public class AthreosGodOfPassage extends CardImpl { effect.setText("As long as your devotion to white and black is less than seven, Athreos isn't a creature"); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); // Whenever another creature you own dies, return it to your hand unless target opponent pays 3 life. - Ability ability = new AthreosDiesCreatureTriggeredAbility(new AthreosGodOfPassageReturnEffect(), false, filter, true); + Ability ability = new AthreosDiesCreatureTriggeredAbility(new AthreosGodOfPassageReturnEffect(), false, filter); ability.addTarget(new TargetOpponent()); this.addAbility(ability); @@ -152,12 +152,10 @@ class AthreosGodOfPassageReturnEffect extends OneShotEffect { class AthreosDiesCreatureTriggeredAbility extends TriggeredAbilityImpl { protected FilterCreaturePermanent filter; - private boolean setTargetPointer; - public AthreosDiesCreatureTriggeredAbility(Effect effect, boolean optional, FilterCreaturePermanent filter, boolean setTargetPointer) { + public AthreosDiesCreatureTriggeredAbility(Effect effect, boolean optional, FilterCreaturePermanent filter) { super(Zone.BATTLEFIELD, effect, optional); this.filter = filter; - this.setTargetPointer = setTargetPointer; } public AthreosDiesCreatureTriggeredAbility(AthreosDiesCreatureTriggeredAbility ability) { @@ -177,10 +175,8 @@ class AthreosDiesCreatureTriggeredAbility extends TriggeredAbilityImpl { if (zEvent.getFromZone().equals(Zone.BATTLEFIELD) && zEvent.getToZone().equals(Zone.GRAVEYARD)) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); if (permanent != null && filter.match(permanent, sourceId, controllerId, game)) { - if (setTargetPointer) { - for (Effect effect : this.getEffects()) { - effect.setValue("creatureId", event.getTargetId()); - } + for (Effect effect : this.getEffects()) { + effect.setValue("creatureId", event.getTargetId()); } return true; } From e3dbe512555c2b6776f0465e9b775a2781b7e598 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Jul 2014 23:54:08 +0200 Subject: [PATCH 17/20] * Siege Draon - Fixed that damage effect did not work if laneswalker was attacked. --- .../src/mage/sets/magic2015/SiegeDragon.java | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2015/SiegeDragon.java b/Mage.Sets/src/mage/sets/magic2015/SiegeDragon.java index 0ff9f25ef14..8720cf21b03 100644 --- a/Mage.Sets/src/mage/sets/magic2015/SiegeDragon.java +++ b/Mage.Sets/src/mage/sets/magic2015/SiegeDragon.java @@ -96,6 +96,12 @@ public class SiegeDragon extends CardImpl { class SiegeDragonAttacksTriggeredAbility extends TriggeredAbilityImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("wall"); + + static { + filter.add(new SubtypePredicate("Wall")); + } + public SiegeDragonAttacksTriggeredAbility() { super(Zone.BATTLEFIELD, new SiegeDragonDamageEffect()); } @@ -111,14 +117,13 @@ class SiegeDragonAttacksTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED && event.getSourceId().equals(this.getSourceId())) { - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new ControllerIdPredicate(event.getTargetId())); - filter.add(new SubtypePredicate("Wall")); - List permanents = game.getBattlefield().getActivePermanents(filter, this.getControllerId(), this.getSourceId(), game); - return permanents.isEmpty(); - } - return false; + return GameEvent.EventType.ATTACKER_DECLARED.equals(event.getType()) && event.getSourceId().equals(this.getSourceId()); + } + + @Override + public boolean checkInterveningIfClause(Game game) { + UUID defendingPlayerId = game.getCombat().getDefendingPlayerId(getSourceId(), game); + return defendingPlayerId != null && game.getBattlefield().countAll(filter, defendingPlayerId, game) < 1; } @Override @@ -144,10 +149,10 @@ class SiegeDragonDamageEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID defenderId = game.getCombat().getDefenderId(source.getSourceId()); - if (defenderId != null) { + UUID defendingPlayerId = game.getCombat().getDefendingPlayerId(source.getSourceId(), game); + if (defendingPlayerId != null) { FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new ControllerIdPredicate(defenderId)); + filter.add(new ControllerIdPredicate(defendingPlayerId)); filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game); for (Permanent permanent : permanents) { From 170920de6b471433f5df261e7a6aa23dfea52eae Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Jul 2014 23:54:52 +0200 Subject: [PATCH 18/20] * Chain of Vapor - Fixed that wrong player was asked for target change of copied spell. --- .../src/mage/sets/onslaught/ChainOfVapor.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/sets/onslaught/ChainOfVapor.java b/Mage.Sets/src/mage/sets/onslaught/ChainOfVapor.java index d0a4dc83ebc..992ad4e7c29 100644 --- a/Mage.Sets/src/mage/sets/onslaught/ChainOfVapor.java +++ b/Mage.Sets/src/mage/sets/onslaught/ChainOfVapor.java @@ -89,31 +89,34 @@ class ChainOfVaporEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { - if(!permanent.moveToZone(Zone.HAND, source.getId(), game, false)){ + if (!controller.moveCardToHandWithInfo(permanent, source.getSourceId(), game, Zone.BATTLEFIELD)){ return false; - } - + } Player player = game.getPlayer(permanent.getControllerId()); - if(player.chooseUse(Outcome.ReturnToHand, "Sacrifice a land to copy this spell?", game)){ + if (player.chooseUse(Outcome.ReturnToHand, "Sacrifice a land to copy this spell?", game)){ TargetControlledPermanent target = new TargetControlledPermanent(new FilterControlledLandPermanent()); - if(player.chooseTarget(Outcome.Sacrifice, target, source, game)){ + if (player.chooseTarget(Outcome.Sacrifice, target, source, game)){ Permanent land = game.getPermanent(target.getFirstTarget()); if(land != null){ if(land.sacrifice(source.getId(), game)){ - Spell spell = game.getStack().getSpell(source.getId()); + Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { Spell copy = spell.copySpell(); copy.setControllerId(player.getId()); copy.setCopiedSpell(true); game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); + copy.chooseNewTargets(game, player.getId()); String activateMessage = copy.getActivatedMessage(game); if (activateMessage.startsWith(" casts ")) { activateMessage = activateMessage.substring(6); } - game.informPlayers(player.getName() + " copies " + activateMessage);; + game.informPlayers(player.getName() + " copies " + activateMessage); return true; } return false; From 0aa263cd55339f3973226f1f3a86908d7b07122e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 12 Jul 2014 23:58:33 +0200 Subject: [PATCH 19/20] * Transmute - Fied that transmute could be activated wrongy as instant instead of only at sorcery time. --- Mage/src/mage/abilities/keyword/TransmuteAbility.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mage/src/mage/abilities/keyword/TransmuteAbility.java b/Mage/src/mage/abilities/keyword/TransmuteAbility.java index 5b7f753ee01..1289bad9819 100644 --- a/Mage/src/mage/abilities/keyword/TransmuteAbility.java +++ b/Mage/src/mage/abilities/keyword/TransmuteAbility.java @@ -18,6 +18,7 @@ import mage.players.Player; import mage.target.common.TargetCardInLibrary; import java.util.UUID; +import mage.constants.TimingRule; /** * @author Loki @@ -25,6 +26,7 @@ import java.util.UUID; public class TransmuteAbility extends SimpleActivatedAbility { public TransmuteAbility(String manaCost) { super(Zone.HAND, new TransmuteEffect(), new ManaCostsImpl(manaCost)); + this.setTiming(TimingRule.SORCERY); this.addCost(new DiscardSourceCost()); } From 613145b17939a47dc1cfb9366ead81c302af75fc Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 13 Jul 2014 09:59:00 +0200 Subject: [PATCH 20/20] * Back to Basics - Fixed that the skip effect was applied to all non basic permanents instead of only non basic lands. --- Mage.Sets/src/mage/sets/urzassaga/BackToBasics.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/urzassaga/BackToBasics.java b/Mage.Sets/src/mage/sets/urzassaga/BackToBasics.java index ca8fcad3732..7bf71a3c1f0 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/BackToBasics.java +++ b/Mage.Sets/src/mage/sets/urzassaga/BackToBasics.java @@ -36,7 +36,7 @@ import mage.constants.Duration; import mage.constants.Rarity; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.SupertypePredicate; @@ -46,7 +46,7 @@ import mage.filter.predicate.mageobject.SupertypePredicate; */ public class BackToBasics extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Nonbasic lands"); + private static final FilterLandPermanent filter = new FilterLandPermanent("Nonbasic lands"); static { filter.add(Predicates.not(new SupertypePredicate("Basic")));