diff --git a/Mage.Client/src/mage/client/cards/Card.java b/Mage.Client/src/mage/client/cards/Card.java index d76ea699aec..57d7b90245c 100644 --- a/Mage.Client/src/mage/client/cards/Card.java +++ b/Mage.Client/src/mage/client/cards/Card.java @@ -34,6 +34,17 @@ package mage.client.cards; +import static mage.client.util.Constants.CONTENT_MAX_XOFFSET; +import static mage.client.util.Constants.FRAME_MAX_HEIGHT; +import static mage.client.util.Constants.FRAME_MAX_WIDTH; +import static mage.client.util.Constants.NAME_FONT_MAX_SIZE; +import static mage.client.util.Constants.NAME_MAX_YOFFSET; +import static mage.client.util.Constants.POWBOX_TEXT_MAX_LEFT; +import static mage.client.util.Constants.POWBOX_TEXT_MAX_TOP; +import static mage.client.util.Constants.SYMBOL_MAX_XOFFSET; +import static mage.client.util.Constants.SYMBOL_MAX_YOFFSET; +import static mage.client.util.Constants.TYPE_MAX_YOFFSET; + import java.awt.Color; import java.awt.Dimension; import java.awt.Font; @@ -52,6 +63,7 @@ import java.awt.event.MouseMotionListener; import java.awt.image.BufferedImage; import java.util.List; import java.util.UUID; + import javax.swing.JScrollPane; import javax.swing.Popup; import javax.swing.PopupFactory; @@ -60,17 +72,18 @@ import javax.swing.text.Style; import javax.swing.text.StyleConstants; import javax.swing.text.StyleContext; import javax.swing.text.StyledDocument; + import mage.Constants.CardType; import mage.client.MageFrame; +import mage.client.game.PlayAreaPanel; import mage.client.remote.Session; +import mage.client.util.ArrowBuilder; import mage.client.util.Config; - import mage.client.util.ImageHelper; import mage.sets.Sets; import mage.view.AbilityView; import mage.view.CardView; import mage.view.StackAbilityView; -import static mage.client.util.Constants.*; /** * @@ -205,7 +218,7 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou for (String rule: getRules()) { sb.append("\n").append(rule); } - if (card.getExpansionSetCode().length() > 0) { + if (card.getExpansionSetCode() != null && card.getExpansionSetCode().length() > 0) { sb.append("\n").append(Sets.getInstance().get(card.getExpansionSetCode()).getName()).append(" - ").append(card.getRarity().toString()); } } @@ -350,6 +363,28 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou popup = factory.getPopup(this, popupText, (int) this.getLocationOnScreen().getX() + Config.dimensions.frameWidth, (int) this.getLocationOnScreen().getY() + 40); popup.show(); popupShowing = true; + + // Draw Arrows for targets + UUID uuid = card.getFirstTarget(); + if (uuid != null) { + System.out.println("Getting play area panel for uuid: " + uuid); + + PlayAreaPanel p = session.getGame().getPlayers().get(uuid); + if (p != null) { + Point target = p.getLocationOnScreen(); + Point me = this.getLocationOnScreen(); + ArrowBuilder.addArrow((int)me.getX() + 35, (int)me.getY(), (int)target.getX() + 40, (int)target.getY() - 40, Color.red); + } else { + for (PlayAreaPanel pa : session.getGame().getPlayers().values()) { + Permanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); + if (permanent != null) { + Point target = permanent.getLocationOnScreen(); + Point me = this.getLocationOnScreen(); + ArrowBuilder.addArrow((int)me.getX() + 35, (int)me.getY(), (int)target.getX() + 40, (int)target.getY() + 10, Color.red); + } + } + } + } } } @@ -359,6 +394,7 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou if (popup != null) { popup.hide(); popupShowing = false; + ArrowBuilder.removeAllArrows(); } } diff --git a/Mage.Client/src/mage/client/game/BattlefieldPanel.java b/Mage.Client/src/mage/client/game/BattlefieldPanel.java index b041dd9cfc6..d0b47c54af2 100644 --- a/Mage.Client/src/mage/client/game/BattlefieldPanel.java +++ b/Mage.Client/src/mage/client/game/BattlefieldPanel.java @@ -153,6 +153,10 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane implements Compon public boolean isOptimizedDrawingEnabled () { return false; } + + public Map getPermanents() { + return permanents; + } /** This method is called from within the constructor to * initialize the form. diff --git a/Mage.Client/src/mage/client/game/GamePanel.java b/Mage.Client/src/mage/client/game/GamePanel.java index 1ab1d19d160..a56d3097617 100644 --- a/Mage.Client/src/mage/client/game/GamePanel.java +++ b/Mage.Client/src/mage/client/game/GamePanel.java @@ -34,20 +34,27 @@ package mage.client.game; +import java.awt.BorderLayout; import java.awt.GridBagConstraints; -import mage.client.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.logging.Logger; + +import javax.swing.JComponent; import javax.swing.JLayeredPane; import javax.swing.JOptionPane; import javax.swing.JPanel; + +import mage.client.MageFrame; import mage.client.dialog.ExileZoneDialog; import mage.client.dialog.PickChoiceDialog; import mage.client.dialog.ShowCardsDialog; import mage.client.game.FeedbackPanel.FeedbackMode; import mage.client.remote.Session; +import mage.client.util.ArrowBuilder; import mage.client.util.Config; import mage.util.Logging; import mage.view.AbilityPickerView; @@ -65,6 +72,7 @@ public class GamePanel extends javax.swing.JPanel { private final static Logger logger = Logging.getLogger(GamePanel.class.getName()); private Map players = new HashMap(); + //private Map players = new HashMap(); private Map exiles = new HashMap(); private UUID gameId; private UUID playerId; @@ -73,6 +81,29 @@ public class GamePanel extends javax.swing.JPanel { /** Creates new form GamePanel */ public GamePanel() { initComponents(); + + // Override layout (I can't edit generated code) + this.setLayout(new BorderLayout()); + final JLayeredPane j = new JLayeredPane(); + j.add(ArrowBuilder.getArrowsPanel(), JLayeredPane.MODAL_LAYER); + j.setSize(1024,768); + //j.setBorder(BorderFactory.createLineBorder(Color.green)); + j.add(jSplitPane1, JLayeredPane.DEFAULT_LAYER); + this.add(j); + + // Enlarge jlayeredpane on resize + addComponentListener(new ComponentAdapter(){ + @Override + public void componentResized(ComponentEvent e) { + int width = ((JComponent)e.getSource()).getWidth(); + int height = ((JComponent)e.getSource()).getHeight(); + j.setSize(width, height); + JPanel arrowsPanel = ArrowBuilder.getArrowsPanelRef(); + if (arrowsPanel != null) arrowsPanel.setSize(width, height); + jSplitPane1.setSize(width, height); + } + }); + } public void cleanUp() { @@ -229,7 +260,9 @@ public class GamePanel extends javax.swing.JPanel { for (PlayerView player: game.getPlayers()) { players.get(player.getPlayerId()).update(player); } + this.stack.loadCards(game.getStack(), bigCard, gameId); + for (ExileView exile: game.getExile()) { if (!exiles.containsKey(exile.getId())) { ExileZoneDialog newExile = new ExileZoneDialog(); @@ -325,6 +358,14 @@ public class GamePanel extends javax.swing.JPanel { pickChoice.showDialog(message, choices); session.sendPlayerString(gameId, pickChoice.getChoice()); } + + public Map getPlayers() { + return players; + } + + public javax.swing.JPanel getBattlefield() { + return pnlBattlefield; + } /** This method is called from within the constructor to * initialize the form. @@ -620,7 +661,7 @@ public class GamePanel extends javax.swing.JPanel { private javax.swing.JLabel lblStep; private javax.swing.JLabel lblTurn; private javax.swing.JPanel pnlBattlefield; - private javax.swing.JPanel pnlGameInfo; + private javax.swing.JPanel pnlGameInfo; private javax.swing.JPanel pnlReplay; private mage.client.cards.Cards stack; private javax.swing.JLabel txtActivePlayer; diff --git a/Mage.Client/src/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/mage/client/game/PlayAreaPanel.java index 6c43ab47594..182dc837126 100644 --- a/Mage.Client/src/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/mage/client/game/PlayAreaPanel.java @@ -83,6 +83,9 @@ public class PlayAreaPanel extends javax.swing.JPanel { this.battlefieldPanel.update(player.getBattlefield()); } + public mage.client.game.BattlefieldPanel getBattlefieldPanel() { + return battlefieldPanel; + } /** This method is called from within the constructor to * initialize the form. @@ -160,7 +163,7 @@ public class PlayAreaPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables private mage.client.game.BattlefieldPanel battlefieldPanel; - private javax.swing.JButton btnCheat; + private javax.swing.JButton btnCheat; private javax.swing.JPanel jPanel1; private javax.swing.JScrollPane jScrollPane1; private mage.client.game.ManaPool manaPool; diff --git a/Mage.Client/src/mage/client/util/Arrow.java b/Mage.Client/src/mage/client/util/Arrow.java new file mode 100644 index 00000000000..3075bbe315e --- /dev/null +++ b/Mage.Client/src/mage/client/util/Arrow.java @@ -0,0 +1,141 @@ +package mage.client.util; + +import java.awt.AlphaComposite; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; +import java.awt.geom.Area; +import java.awt.geom.GeneralPath; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +public class Arrow extends JPanel { + private static final long serialVersionUID = -4631054277822828303L; + + private int startX; + private int startY; + private int endX; + private int endY; + private int bodyWidth = 10; + private float headSize = 17; + private Composite composite; + private Color color = Color.red; + + public Arrow () { + setOpaque(false); + setOpacity(0.6f); + } + + protected void paintComponent (Graphics g) { + super.paintComponent(g); + float ex = endX - startX; + float ey = endY - startY; + if (ex == 0 && ey == 0) return; + float length = (float)Math.sqrt(ex * ex + ey * ey); + float bendPercent = (float)Math.asin(ey / length); + if (endX > startX) bendPercent = -bendPercent; + Area arrow = getArrow(length, bendPercent); + Graphics2D g2d = (Graphics2D)g; + g2d.translate(startX, startY); + g2d.rotate(Math.atan2(ey, ex)); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setComposite(composite); + g2d.setColor(this.color); + g2d.fill(arrow); + g2d.setColor(Color.BLACK); + g2d.draw(arrow); + } + + private Area getArrow (float length, float bendPercent) { + float p1x = 0, p1y = 0; + float p2x = length, p2y = 0; + float cx = length / 2, cy = length / 8f * bendPercent; + + float adjSize, ex, ey, abs_e; + adjSize = (float)(bodyWidth / 2 / Math.sqrt(2)); + ex = p2x - cx; + ey = p2y - cy; + abs_e = (float)Math.sqrt(ex * ex + ey * ey); + ex /= abs_e; + ey /= abs_e; + GeneralPath bodyPath = new GeneralPath(); + bodyPath.moveTo(p2x + (ey - ex) * adjSize, p2y - (ex + ey) * adjSize); + bodyPath.quadTo(cx, cy, p1x, p1y - bodyWidth / 2); + bodyPath.lineTo(p1x, p1y + bodyWidth / 2); + bodyPath.quadTo(cx, cy, p2x - (ey + ex) * adjSize, p2y + (ex - ey) * adjSize); + bodyPath.closePath(); + + adjSize = (float)(headSize / Math.sqrt(2)); + ex = p2x - cx; + ey = p2y - cy; + abs_e = (float)Math.sqrt(ex * ex + ey * ey); + ex /= abs_e; + ey /= abs_e; + GeneralPath headPath = new GeneralPath(); + headPath.moveTo(p2x - (ey + ex) * adjSize, p2y + (ex - ey) * adjSize); + headPath.lineTo(p2x, p2y); + headPath.lineTo(p2x + (ey - ex) * adjSize, p2y - (ex + ey) * adjSize); + headPath.closePath(); + + Area area = new Area(headPath); + area.add(new Area(bodyPath)); + return area; + } + + public int getBodyWidth () { + return bodyWidth; + } + + public void setBodyWidth (int bodyWidth) { + this.bodyWidth = bodyWidth; + } + + public float getHeadSize () { + return headSize; + } + + public void setHeadSize (float headSize) { + this.headSize = headSize; + } + + public void setArrowLocation (int startX, int startY, int endX, int endY) { + this.startX = startX; + this.startY = startY; + this.endX = endX; + this.endY = endY; + repaint(); + } + + public void setOpacity (float opacity) { + composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity); + } + + public void setColor(Color color) { + this.color = color; + } + + public static void main (String[] args) { + final JFrame frame = new JFrame(); + frame.setLayout(new BorderLayout()); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + final Arrow arrow = new Arrow(); + frame.add(arrow, BorderLayout.CENTER); + frame.setSize(640, 480); + frame.setResizable(false); + frame.setVisible(true); + frame.getContentPane().addMouseMotionListener(new MouseMotionListener() { + public void mouseMoved (MouseEvent e) { + arrow.setArrowLocation(320, 240, e.getX(), e.getY()); + } + + public void mouseDragged (MouseEvent e) { + } + }); + } +} diff --git a/Mage.Client/src/mage/client/util/ArrowBuilder.java b/Mage.Client/src/mage/client/util/ArrowBuilder.java new file mode 100644 index 00000000000..5c9593a7f05 --- /dev/null +++ b/Mage.Client/src/mage/client/util/ArrowBuilder.java @@ -0,0 +1,83 @@ +package mage.client.util; + +import java.awt.BorderLayout; +import java.awt.Color; + +import javax.swing.BorderFactory; +import javax.swing.JInternalFrame; +import javax.swing.JPanel; + +/** + * Class for dealing with arrows in the game. + * + * @author nantuko + */ +public class ArrowBuilder { + + public static JPanel arrowsPanel; + + /** + * Get the panel where all arrows are being drawn. + * @return + */ + public static JPanel getArrowsPanel() { + if (arrowsPanel == null) { + synchronized (ArrowBuilder.class) { + if (arrowsPanel == null) { + arrowsPanel = new JPanel(); + arrowsPanel.setVisible(true); + arrowsPanel.setOpaque(false); + //arrowsPanel.setLayout(new BorderLayout()); + arrowsPanel.setLayout(null); + //arrowsPanel.setBorder(BorderFactory.createLineBorder(Color.red)); + } + } + } + return arrowsPanel; + } + + /** + * Not synchronized method for arrows panel. + * Doesn't create JPanel in case the panel doesn't exist. + * Works faster. + * + * @return + */ + public static JPanel getArrowsPanelRef() { + return arrowsPanel; + } + + /** + * Adds new arrow. + * + * @param startX + * @param startY + * @param endX + * @param endY + * @param color + */ + public static void addArrow(int startX, int startY, int endX, int endY, Color color) { + JPanel p = getArrowsPanel(); + Arrow arrow = new Arrow(); + arrow.setColor(color); + arrow.setArrowLocation(startX, startY, endX, endY); + arrow.setBounds(0, 0, Math.max(startX, endX) + 30, Math.max(startY, endY) + 30); // 30 is offset for arrow heads (being cut otherwise) + System.out.println(" Adding arrow (startX=" + startX + ", startY=" + startY +", endX=" + endX + ", endY=" + endY + ")"); + p.add(arrow); + p.revalidate(); + p.repaint(); + } + + /** + * Removes all arrows from the screen. + */ + public static void removeAllArrows() { + JPanel p = getArrowsPanel(); + if (p.getComponentCount() > 0) { + p.removeAll(); + p.revalidate(); + p.repaint(); + } + } + +} diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index e0ee9051411..aaefb3c58ea 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -32,13 +32,17 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.UUID; + +import mage.ObjectColor; import mage.Constants.CardType; import mage.Constants.Rarity; -import mage.ObjectColor; +import mage.abilities.Ability; import mage.cards.Card; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; import mage.game.permanent.token.Token; +import mage.game.stack.Spell; +import mage.target.Target; /** * @@ -61,6 +65,8 @@ public class CardView implements Serializable { protected Rarity rarity; protected String expansionSetCode; + public UUID firstTarget; //TODO: there may be several targets + public CardView(Card card) { this.id = card.getId(); this.name = card.getName(); @@ -89,6 +95,17 @@ public class CardView implements Serializable { this.rarity = card.getRarity(); this.expansionSetCode = card.getExpansionSetCode(); } + + if (card instanceof Spell) { + Spell spell = (Spell)card; + if (spell.getSpellAbility().getTargets().size() > 0) { + Target target = spell.getSpellAbility().getTargets().get(0); + if (target.isChosen()) { + firstTarget = target.getFirstTarget(); + System.out.println("First target: " + firstTarget); + } + } + } } protected CardView() { @@ -181,4 +198,8 @@ public class CardView implements Serializable { public UUID getId() { return id; } + + public UUID getFirstTarget() { + return firstTarget; + } } diff --git a/Mage/src/mage/cards/basiclands/BasicLand.java b/Mage/src/mage/cards/basiclands/BasicLand.java index 92c718c93e0..95f7df34985 100644 --- a/Mage/src/mage/cards/basiclands/BasicLand.java +++ b/Mage/src/mage/cards/basiclands/BasicLand.java @@ -45,6 +45,7 @@ public abstract class BasicLand> extends CardImpl { this.supertype.add("Basic"); this.subtype.add(name); this.addAbility(mana); + setExpansionSetCode("M11"); } public BasicLand(BasicLand land) {