Most obvious bugs ironed out. Ready for PR.

This commit is contained in:
Mark Langen 2016-08-31 23:37:31 -06:00
parent d5415d2d04
commit d33f8a636e
19 changed files with 415 additions and 101 deletions

View file

@ -222,7 +222,7 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis
}
@Override
public void updateImage() {
public void updateArtImage() {
}

View file

@ -114,7 +114,7 @@ public class Cards extends javax.swing.JPanel {
setGUISize();
for (MageCard mageCard : cards.values()) {
mageCard.setCardBounds(0, 0, getCardDimension().width, getCardDimension().height);
mageCard.updateImage();
mageCard.updateArtImage();
mageCard.doLayout();
}
layoutCards();

View file

@ -223,7 +223,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
// this calls updateImage
toggleTransformed();
} else {
updateImage();
updateArtImage();
}
}
@ -372,7 +372,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
setBounds(x - cardXOffset, y - cardYOffset, getWidth(), getHeight());
return;
}
this.cardWidth = cardWidth;
this.symbolWidth = cardWidth / 7;
this.cardHeight = cardHeight;
@ -389,8 +389,8 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
int height = -yOffset + rotCenterY + rotCenterToBottomCorner;
setBounds(x + xOffset, y + yOffset, width, height);
} else {
cardXOffset = 5;
cardYOffset = 5;
cardXOffset = 0;
cardYOffset = 0;
int width = cardXOffset * 2 + cardWidth;
int height = cardYOffset * 2 + cardHeight;
setBounds(x - cardXOffset, y - cardYOffset, width, height);
@ -538,6 +538,11 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
// Update panel attributes
this.isChoosable = card.isChoosable();
this.isSelected = card.isSelected();
// Update art?
boolean mustUpdateArt =
(!gameCard.getName().equals(card.getName())) ||
(gameCard.isFaceDown() != card.isFaceDown());
// Set the new card
this.gameCard = card;
@ -546,8 +551,12 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
String cardType = getType(card);
tooltipText.setText(getText(cardType, card));
// Update the image and transform circle if needed
updateImage();
// Update the image
if (mustUpdateArt) {
updateArtImage();
}
// Update transform circle
if (card.canTransform()) {
BufferedImage transformIcon;
if (isTransformed() || card.isTransformed()) {
@ -783,7 +792,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
String temp = this.gameCard.getAlternateName();
this.gameCard.setAlternateName(this.gameCard.getOriginalName());
this.gameCard.setOriginalName(temp);
updateImage();
updateArtImage();
}
@Override

View file

@ -0,0 +1,25 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.card.arcane;
/**
* @author stravant@gmail.com
* Attributes of a card panel outside of the CardView itself that the renderer
* needs to know in order to render a card.
*/
public class CardPanelAttributes {
public final int cardWidth;
public final int cardHeight;
public final boolean isSelected;
public final boolean isChoosable;
public CardPanelAttributes(int cardWidth, int cardHeight, boolean isChoosable, boolean isSelected) {
this.cardWidth = cardWidth;
this.cardHeight = cardHeight;
this.isChoosable = isChoosable;
this.isSelected = isSelected;
}
}

View file

@ -503,7 +503,7 @@ public class CardPanelComponentImpl extends CardPanel {
// Update image
if (imagePanel != null && imagePanel.getSrcImage() != null) {
updateImage();
updateArtImage();
}
}
@ -523,15 +523,15 @@ public class CardPanelComponentImpl extends CardPanel {
///////////////////////////////////////////////////////////
// Image updating code
private int updateImageStamp;
private int updateArtImageStamp;
@Override
public void updateImage() {
public void updateArtImage() {
tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0;
flippedAngle = isFlipped() ? CardPanel.FLIPPED_ANGLE : 0;
//final CardView gameCard = this.gameCard;
final int stamp = ++updateImageStamp;
final int stamp = ++updateArtImageStamp;
Util.threadPool.submit(new Runnable() {
@Override
@ -548,7 +548,7 @@ public class CardPanelComponentImpl extends CardPanel {
UI.invokeLater(new Runnable() {
@Override
public void run() {
if (stamp == updateImageStamp) {
if (stamp == updateArtImageStamp) {
hasImage = srcImage != null;
setText(gameCard);
setImage(srcImage);

View file

@ -14,6 +14,8 @@ import mage.cards.action.ActionCallback;
import mage.client.util.ImageCaches;
import mage.constants.CardType;
import mage.view.CardView;
import mage.view.CounterView;
import mage.view.PermanentView;
import org.apache.log4j.Logger;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import static org.mage.plugins.card.constants.Constants.THUMBNAIL_SIZE_FULL;
@ -27,6 +29,9 @@ public class CardPanelRenderImpl extends CardPanel {
if (a == b) {
return true;
}
if (a.getClass() != b.getClass()) {
return false;
}
if (!a.getName().equals(b.getName())) {
return false;
}
@ -60,6 +65,28 @@ public class CardPanelRenderImpl extends CardPanel {
if (!a.getExpansionSetCode().equals(b.getExpansionSetCode())) {
return false;
}
if (a.getCounters() == null) {
if (b.getCounters() != null) {
return false;
}
} else if (!a.getCounters().equals(b.getCounters())) {
return false;
}
if (a.isFaceDown() != b.isFaceDown()) {
return false;
}
if ((a instanceof PermanentView)) {
PermanentView aa = (PermanentView)a;
PermanentView bb = (PermanentView)b;
if (aa.hasSummoningSickness() != bb.hasSummoningSickness()) {
// Note: b must be a permanentview too as we aleady checked that classes
// are the same for a and b
return false;
}
if (aa.getDamage() != bb.getDamage()) {
return false;
}
}
return true;
}
@ -91,6 +118,11 @@ public class CardPanelRenderImpl extends CardPanel {
sb.append((char)(isChoosable ? 1 : 0));
sb.append((char)(this.view.isPlayable() ? 1 : 0));
sb.append((char)(this.view.isCanAttack() ? 1 : 0));
sb.append((char)(this.view.isFaceDown() ? 1 : 0));
if (this.view instanceof PermanentView) {
sb.append((char)(((PermanentView)this.view).hasSummoningSickness() ? 1 : 0));
sb.append((char)(((PermanentView)this.view).getDamage()));
}
sb.append(this.view.getName());
sb.append(this.view.getPower());
sb.append(this.view.getToughness());
@ -112,6 +144,11 @@ public class CardPanelRenderImpl extends CardPanel {
for (String s: this.view.getRules()) {
sb.append(s);
}
if (this.view.getCounters() != null) {
for (CounterView v: this.view.getCounters()) {
sb.append(v.getName()).append(v.getCount());
}
}
return sb.toString().hashCode();
}
@ -207,7 +244,7 @@ public class CardPanelRenderImpl extends CardPanel {
}
// And draw the image we now have
g.drawImage(cardImage, 0, 0, null);
g.drawImage(cardImage, getCardXOffset(), getCardYOffset(), null);
}
/**
@ -217,33 +254,34 @@ public class CardPanelRenderImpl extends CardPanel {
private BufferedImage renderCard() {
int cardWidth = getCardWidth();
int cardHeight = getCardHeight();
int cardXOffset = getCardXOffset();
int cardYOffset = getCardYOffset();
// Create image to render to
BufferedImage image =
GraphicsUtilities.createCompatibleTranslucentImage(getWidth(), getHeight());
GraphicsUtilities.createCompatibleTranslucentImage(cardWidth, cardHeight);
Graphics2D g2d = image.createGraphics();
// Render with Antialialsing
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Attributes
CardPanelAttributes attribs =
new CardPanelAttributes(cardWidth, cardHeight, isChoosable(), isSelected());
// Draw card itself
g2d.translate(cardXOffset, cardYOffset);
cardRenderer.draw(g2d, cardWidth - cardXOffset, cardHeight - cardYOffset);
g2d.translate(-cardXOffset, -cardYOffset);
cardRenderer.draw(g2d, attribs);
// Done
g2d.dispose();
return image;
}
private int updateImageStamp;
private int updateArtImageStamp;
@Override
public void updateImage() {
public void updateArtImage() {
// Invalidate
artImage = null;
cardImage = null;
cardRenderer.setArtImage(null);
// Stop animation
tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0;
@ -251,43 +289,49 @@ public class CardPanelRenderImpl extends CardPanel {
// Schedule a repaint
repaint();
// See if the image is already loaded
//artImage = ImageCache.tryGetImage(gameCard, getCardWidth(), getCardHeight());
//this.cardRenderer.setArtImage(artImage);
// Submit a task to draw with the card art when it arrives
final int stamp = ++updateImageStamp;
Util.threadPool.submit(new Runnable() {
@Override
public void run() {
try {
final BufferedImage srcImage;
if (gameCard.isFaceDown()) {
// Nothing to do
srcImage = null;
} else if (getCardWidth() > THUMBNAIL_SIZE_FULL.width) {
srcImage = ImageCache.getImage(gameCard, getCardWidth(), getCardHeight());
} else {
srcImage = ImageCache.getThumbnail(gameCard);
}
UI.invokeLater(new Runnable() {
@Override
public void run() {
if (stamp == updateImageStamp) {
CardPanelRenderImpl.this.artImage = srcImage;
CardPanelRenderImpl.this.cardRenderer.setArtImage(srcImage);
if (srcImage != null) {
// Invalidate and repaint
CardPanelRenderImpl.this.cardImage = null;
repaint();
if (artImage == null) {
final int stamp = ++updateArtImageStamp;
Util.threadPool.submit(new Runnable() {
@Override
public void run() {
try {
final BufferedImage srcImage;
if (gameCard.isFaceDown()) {
// Nothing to do
srcImage = null;
} else if (getCardWidth() > THUMBNAIL_SIZE_FULL.width) {
srcImage = ImageCache.getImage(gameCard, getCardWidth(), getCardHeight());
} else {
srcImage = ImageCache.getThumbnail(gameCard);
}
UI.invokeLater(new Runnable() {
@Override
public void run() {
if (stamp == updateArtImageStamp) {
artImage = srcImage;
cardRenderer.setArtImage(srcImage);
if (srcImage != null) {
// Invalidate and repaint
cardImage = null;
repaint();
}
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
} catch (Error err) {
err.printStackTrace();
});
} catch (Exception e) {
e.printStackTrace();
} catch (Error err) {
err.printStackTrace();
}
}
}
});
});
}
}
@Override
@ -296,7 +340,9 @@ public class CardPanelRenderImpl extends CardPanel {
super.update(card);
// Update renderer
cardImage = null;
cardRenderer = new ModernCardRenderer(gameCard, isTransformed());
cardRenderer.setArtImage(artImage);
// Repaint
repaint();
@ -304,10 +350,15 @@ public class CardPanelRenderImpl extends CardPanel {
@Override
public void setCardBounds(int x, int y, int cardWidth, int cardHeight) {
int oldCardWidth = getCardWidth();
int oldCardHeight = getCardHeight();
super.setCardBounds(x, y, cardWidth, cardHeight);
// Rerender
cardImage = null;
// Rerender if card size changed
if (getCardWidth() != oldCardWidth || getCardHeight() != oldCardHeight) {
cardImage = null;
}
}
@Override

View file

@ -18,9 +18,11 @@ import java.awt.image.BufferedImage;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.List;
import mage.constants.AbilityType;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.counters.Counter;
import mage.utils.CardUtil;
import mage.view.CardView;
import mage.view.CounterView;
import mage.view.PermanentView;
@ -108,6 +110,10 @@ public abstract class CardRenderer {
protected int cardWidth;
protected int cardHeight;
// Is it selectable / selected
protected boolean isChoosable;
protected boolean isSelected;
// Radius of the corners of the cards
protected static float CORNER_RADIUS_FRAC = 0.1f; //x cardWidth
protected static int CORNER_RADIUS_MIN = 3;
@ -164,17 +170,21 @@ public abstract class CardRenderer {
// The Draw Method
// The draw method takes the information caculated by the constructor
// and uses it to draw to a concrete size of card and graphics.
public void draw(Graphics2D g, int cardWidth, int cardHeight) {
public void draw(Graphics2D g, CardPanelAttributes attribs) {
// Pre template method layout, to calculate shared layout info
layout(cardWidth, cardHeight);
layout(attribs.cardWidth, attribs.cardHeight);
isSelected = attribs.isSelected;
isChoosable = attribs.isChoosable;
// Call the template methods
drawBorder(g);
drawBackground(g);
drawArt(g);
drawFrame(g);
drawOverlays(g);
drawCounters(g);
if (!cardView.isAbility()) {
drawOverlays(g);
drawCounters(g);
}
}
// Template methods to be implemented by sub classes
@ -196,7 +206,7 @@ public abstract class CardRenderer {
// Draw summoning sickness overlay, and possibly other overlays
protected void drawOverlays(Graphics2D g) {
if (cardView instanceof PermanentView) {
if (CardUtil.isCreature(cardView) && cardView instanceof PermanentView) {
if (((PermanentView)cardView).hasSummoningSickness()) {
int x1 = (int)(0.2*cardWidth);
int x2 = (int)(0.8*cardWidth);
@ -229,7 +239,7 @@ public abstract class CardRenderer {
// Draw +1/+1 and other counters
protected void drawCounters(Graphics2D g) {
int xPos = (int)(0.7*cardWidth);
int xPos = (int)(0.65*cardWidth);
int yPos = (int)(0.15*cardHeight);
if (cardView.getCounters() != null) {
for (CounterView v: cardView.getCounters()) {
@ -245,7 +255,7 @@ public abstract class CardRenderer {
} else {
p = OTHER_COUNTER_POLY;
}
double scale = (0.1*0.18*cardWidth);
double scale = (0.1*0.25*cardWidth);
Graphics2D g2 = (Graphics2D)g.create();
g2.translate(xPos, yPos);
g2.scale(scale, scale);
@ -258,7 +268,7 @@ public abstract class CardRenderer {
int strW = g2.getFontMetrics().stringWidth(cstr);
g2.drawString(cstr, 5 - strW/2, 8);
g2.dispose();
yPos += ((int)(0.22*cardWidth));
yPos += ((int)(0.30*cardWidth));
}
}
}
@ -324,20 +334,31 @@ public abstract class CardRenderer {
// Get a string representing the type line
protected String getCardTypeLine() {
StringBuilder sbType = new StringBuilder();
for (String superType : cardView.getSuperTypes()) {
sbType.append(superType).append(" ");
}
for (CardType cardType : cardView.getCardTypes()) {
sbType.append(cardType.toString()).append(" ");
}
if (cardView.getSubTypes().size() > 0) {
sbType.append("- ");
for (String subType : cardView.getSubTypes()) {
sbType.append(subType).append(" ");
if (cardView.isAbility()) {
switch (cardView.getAbilityType()) {
case TRIGGERED:
return "Triggered Ability";
case ACTIVATED:
return "Activated Ability";
default:
return "??? Ability";
}
} else {
StringBuilder sbType = new StringBuilder();
for (String superType : cardView.getSuperTypes()) {
sbType.append(superType).append(" ");
}
for (CardType cardType : cardView.getCardTypes()) {
sbType.append(cardType.toString()).append(" ");
}
if (cardView.getSubTypes().size() > 0) {
sbType.append("- ");
for (String subType : cardView.getSubTypes()) {
sbType.append(subType).append(" ");
}
}
return sbType.toString();
}
return sbType.toString();
}
// Set the card art image (CardPanel will give it to us when it

View file

@ -8,6 +8,7 @@ package org.mage.card.arcane;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Image;
@ -22,7 +23,10 @@ import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.font.TextMeasurer;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
@ -35,6 +39,7 @@ import mage.ObjectColor;
import mage.constants.CardType;
import mage.view.CardView;
import mage.view.PermanentView;
import net.java.balloontip.styles.RoundedBalloonStyle;
import org.apache.log4j.Logger;
import org.mage.card.arcane.CardRenderer;
import org.mage.card.arcane.CardRendererUtils;
@ -82,6 +87,20 @@ public class ModernCardRenderer extends CardRenderer {
BufferedImage img = CardRendererUtils.toBufferedImage(icon.getImage());
return new TexturePaint(img, new Rectangle(0, 0, img.getWidth(), img.getHeight()));
}
private static Font loadFont(String name) {
try {
return Font.createFont(
Font.TRUETYPE_FONT,
ModernCardRenderer.class.getResourceAsStream("/cardrender/" + name + ".ttf"));
} catch (IOException e) {
LOGGER.info("Failed to load font `" + name + "`, couldn't find resource.");
} catch (FontFormatException e) {
LOGGER.info("Failed to load font `" + name + "`, bad format.");
}
return new Font("Arial", Font.PLAIN, 1);
}
public static Font BASE_BELEREN_FONT = loadFont("beleren-bold");
public static Paint BG_TEXTURE_WHITE = loadBackgroundTexture("white");
public static Paint BG_TEXTURE_BLUE = loadBackgroundTexture("blue");
public static Paint BG_TEXTURE_BLACK = loadBackgroundTexture("black");
@ -152,6 +171,7 @@ public class ModernCardRenderer extends CardRenderer {
// How far down the card is the type line placed?
protected static float TYPE_LINE_Y_FRAC = 0.57f; // x cardHeight
protected static float TYPE_LINE_Y_FRAC_TOKEN = 0.70f;
protected int typeLineY;
// How large is the box text, and how far is it down the boxes
@ -195,17 +215,21 @@ public class ModernCardRenderer extends CardRenderer {
BOX_HEIGHT_FRAC * cardHeight);
// Type line at
typeLineY = (int)(TYPE_LINE_Y_FRAC * cardHeight);
if (cardView.isToken()) {
typeLineY = (int)(TYPE_LINE_Y_FRAC_TOKEN * cardHeight);
} else {
typeLineY = (int)(TYPE_LINE_Y_FRAC * cardHeight);
}
// Box text height
boxTextHeight = getTextHeightForBoxHeight(boxHeight);
boxTextOffset = (boxHeight - boxTextHeight)/2;
boxTextFont = new Font("Beleren", Font.PLAIN, boxTextHeight);
boxTextFont = BASE_BELEREN_FONT.deriveFont(Font.PLAIN, boxTextHeight);
// Box text height
ptTextHeight = getPTTextHeightForLineHeight(boxHeight);
ptTextOffset = (boxHeight - ptTextHeight)/2;
ptTextFont = new Font("Beleren", Font.PLAIN, ptTextHeight);
ptTextFont = BASE_BELEREN_FONT.deriveFont(Font.PLAIN, ptTextHeight);
}
@Override
@ -213,6 +237,33 @@ public class ModernCardRenderer extends CardRenderer {
// Draw border as one rounded rectangle
g.setColor(Color.black);
g.fillRoundRect(0, 0, cardWidth, cardHeight, cornerRadius, cornerRadius);
// Selection Borders
Color borderColor;
if (isSelected) {
borderColor = Color.green;
} else if (isChoosable) {
borderColor = new Color(250, 250, 0, 230);
} else if (cardView.isPlayable()) {
borderColor = new Color(153, 102, 204, 200);
} else if (cardView instanceof PermanentView && ((PermanentView)cardView).isCanAttack()) {
borderColor = new Color(0, 0, 255, 230);
} else {
borderColor = null;
}
if (borderColor != null) {
float hwidth = borderWidth / 2.0f;
Graphics2D g2 = (Graphics2D)g.create();
g2.setColor(borderColor);
g2.setStroke(new BasicStroke(borderWidth));
RoundRectangle2D.Float rect
= new RoundRectangle2D.Float(
hwidth, hwidth,
cardWidth - borderWidth, cardHeight - borderWidth,
cornerRadius, cornerRadius);
g2.draw(rect);
g2.dispose();
}
}
@Override
@ -247,7 +298,7 @@ public class ModernCardRenderer extends CardRenderer {
@Override
protected void drawArt(Graphics2D g) {
if (artImage != null) {
if (artImage != null && !cardView.isFaceDown()) {
int imgWidth = artImage.getWidth();
int imgHeight = artImage.getHeight();
BufferedImage subImg =
@ -354,14 +405,29 @@ public class ModernCardRenderer extends CardRenderer {
// Draw the name line
protected void drawNameLine(Graphics2D g, int x, int y, int w, int h) {
// Width of the mana symbols
int manaCostWidth = CardRendererUtils.getManaCostWidth(manaCostString, boxTextHeight);
int manaCostWidth;
if (cardView.isAbility()) {
manaCostWidth = 0;
} else {
manaCostWidth = CardRendererUtils.getManaCostWidth(manaCostString, boxTextHeight);
}
// Available width for name. Add a little bit of slop so that one character
// can partially go underneath the mana cost
int availableWidth = w - manaCostWidth + 2;
// Draw the name
AttributedString str = new AttributedString(cardView.getName());
String nameStr;
if (cardView.isFaceDown()) {
if (cardView instanceof PermanentView && ((PermanentView)cardView).isManifested()) {
nameStr = "Manifest: " + cardView.getName();
} else {
nameStr = "Morph: " + cardView.getName();
}
} else {
nameStr = cardView.getName();
}
AttributedString str = new AttributedString(nameStr);
str.addAttribute(TextAttribute.FONT, boxTextFont);
TextMeasurer measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext());
TextLayout layout = measure.getLayout(0, measure.getLineBreakIndex(0, availableWidth));
@ -369,13 +435,20 @@ public class ModernCardRenderer extends CardRenderer {
layout.draw(g, x, y + boxTextOffset + boxTextHeight - 1);
// Draw the mana symbols
ManaSymbols.draw(g, manaCostString, x + w - manaCostWidth, y + boxTextOffset, boxTextHeight);
if (!cardView.isAbility() && !cardView.isFaceDown()) {
ManaSymbols.draw(g, manaCostString, x + w - manaCostWidth, y + boxTextOffset, boxTextHeight);
}
}
// Draw the type line (color indicator, types, and expansion symbol)
protected void drawTypeLine(Graphics2D g, int x, int y, int w, int h) {
// Draw expansion symbol
int expansionSymbolWidth = drawExpansionSymbol(g, x, y, w, h);
int expansionSymbolWidth;
if (cardView.isAbility()) {
expansionSymbolWidth = 0;
} else {
expansionSymbolWidth = drawExpansionSymbol(g, x, y, w, h);
}
// Draw type line text
int availableWidth = w - expansionSymbolWidth + 1;
@ -387,16 +460,23 @@ public class ModernCardRenderer extends CardRenderer {
types = types.replace("Legendary", "L.");
}
AttributedString str = new AttributedString(types);
str.addAttribute(TextAttribute.FONT, boxTextFont);
TextMeasurer measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext());
TextLayout layout = measure.getLayout(0, measure.getLineBreakIndex(0, availableWidth));
g.setColor(getBoxTextColor());
layout.draw(g, x, y + boxTextOffset + boxTextHeight - 1);
if (!types.isEmpty()) {
AttributedString str = new AttributedString(types);
str.addAttribute(TextAttribute.FONT, boxTextFont);
TextMeasurer measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext());
TextLayout layout = measure.getLayout(0, measure.getLineBreakIndex(0, availableWidth));
g.setColor(getBoxTextColor());
layout.draw(g, x, y + boxTextOffset + boxTextHeight - 1);
}
}
// Draw the P/T and/or Loyalty boxes
protected void drawBottomRight(Graphics2D g, Paint borderPaint, Color fill) {
// No bottom right for abilities
if (cardView.isAbility()) {
return;
}
// Where to start drawing the things
int curY = cardHeight - (int)(0.03f*cardHeight);
@ -493,6 +573,20 @@ public class ModernCardRenderer extends CardRenderer {
// Advance
curY -= (int)(1.2*y);
}
// does it have damage on it?
if ((cardView instanceof PermanentView) && ((PermanentView)cardView).getDamage() > 0) {
int x = cardWidth - partWidth - borderWidth;
int y = curY - boxHeight;
String damage = "" + ((PermanentView)cardView).getDamage();
g.setFont(ptTextFont);
int txWidth = g.getFontMetrics().stringWidth(damage);
g.setColor(Color.red);
g.fillRect(x, y, partWidth, boxHeight);
g.setColor(Color.white);
g.drawRect(x, y, partWidth, boxHeight);
g.drawString(damage, x + (partWidth - txWidth)/2, curY - 1);
}
}
// Draw the card's textbox in a given rect
@ -763,6 +857,8 @@ public class ModernCardRenderer extends CardRenderer {
protected Color getBoxTextColor() {
if (isNightCard()) {
return Color.white;
} else if (cardView.isAbility()) {
return Color.white;
} else {
return Color.black;
}
@ -800,8 +896,10 @@ public class ModernCardRenderer extends CardRenderer {
}
// Get the box color for the given colors
protected static Color getBoxColor(ObjectColor colors, Collection<CardType> types, boolean isNightCard) {
if (colors.getColorCount() == 2 && types.contains(CardType.LAND)) {
protected Color getBoxColor(ObjectColor colors, Collection<CardType> types, boolean isNightCard) {
if (cardView.isAbility()) {
return Color.BLACK;
} else if (colors.getColorCount() == 2 && types.contains(CardType.LAND)) {
// Special case for two color lands. Boxes should be normal land colored
// rather than multicolor. Three or greater color lands use a multi-color
// box as normal.

View file

@ -17,6 +17,7 @@ import java.util.Deque;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import mage.client.dialog.PreferencesDialog;
import mage.view.CardView;
import org.apache.log4j.Logger;
@ -37,6 +38,11 @@ public class TextboxRuleParser {
// representing that information, which can be used to render the rule in
// the textbox of a card.
public static TextboxRule parse(CardView source, String rule) {
// Kill reminder text
if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_REMINDER_TEXT, "false").equals("false")) {
rule = CardRendererUtils.killReminderText(rule);
}
// List of regions to apply
ArrayList<TextboxRule.AttributeRegion> regions = new ArrayList<>();

View file

@ -200,6 +200,10 @@ public class ImageCache {
public static BufferedImage getThumbnail(CardView card) {
return getImage(getKey(card, card.getName(), "#thumb"));
}
public static BufferedImage tryGetThumbnail(CardView card) {
return tryGetImage(getKey(card, card.getName(), "#thumb"));
}
public static BufferedImage getImageOriginal(CardView card) {
return getImage(getKey(card, card.getName(), ""));
@ -230,6 +234,18 @@ public class ImageCache {
return null;
}
}
/**
* Returns the Image corresponding to the key only if it already exists
* in the cache.
*/
private static BufferedImage tryGetImage(String key) {
if (IMAGE_CACHE.containsKey(key)) {
return IMAGE_CACHE.get(key);
} else {
return null;
}
}
/**
* Returns the map key for a card, without any suffixes for the image size.
@ -343,6 +359,34 @@ public class ImageCache {
return TransformedImageCache.getResizedImage(original, (int) (original.getWidth() * scale), (int) (original.getHeight() * scale));
}
/**
* Returns the image appropriate to display for a card in a picture panel, but
* only it was ALREADY LOADED. That is, the call is immediate and will not block
* on file IO.
* @param card
* @param width
* @param height
* @return
*/
public static BufferedImage tryGetImage(CardView card, int width, int height) {
if (Constants.THUMBNAIL_SIZE_FULL.width + 10 > width) {
return tryGetThumbnail(card);
}
String key = getKey(card, card.getName(), Integer.toString(width));
BufferedImage original = tryGetImage(key);
if (original == null) {
LOGGER.debug(key + " not found");
return null;
}
double scale = Math.min((double) width / original.getWidth(), (double) height / original.getHeight());
if (scale >= 1) {
return original;
}
return TransformedImageCache.getResizedImage(original, (int) (original.getWidth() * scale), (int) (original.getHeight() * scale));
}
public static TFile getTFile(String path) {
try {

View file

@ -32,7 +32,7 @@ public abstract class MageCard extends JPanel {
public abstract void update(CardView card);
public abstract void updateImage();
public abstract void updateArtImage();
public abstract Image getImage();

View file

@ -53,4 +53,21 @@ public class CounterView implements Serializable {
public int getCount() {
return count;
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (other == null) {
return false;
}
if (!(other instanceof CounterView)) {
return false;
}
CounterView oth = (CounterView)other;
return
(count == oth.count) &&
(name.equals(oth.name));
}
}

View file

@ -38,6 +38,7 @@ import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.keyword.ChangelingAbility;
import mage.abilities.mana.ManaAbility;
import mage.constants.CardType;
import mage.game.Game;
import mage.util.CardUtil;
@ -176,7 +177,48 @@ public abstract class MageObjectImpl implements MageObject {
@Override
public ObjectColor getFrameColor(Game game) {
return frameColor;
// For lands, add any colors of mana the land can produce to
// its frame colors.
if (getCardType().contains(CardType.LAND)) {
ObjectColor cl = frameColor.copy();
for (Ability ab: getAbilities()) {
if (ab instanceof ManaAbility) {
ManaAbility mana = (ManaAbility)ab;
try {
List<Mana> manaAdded = mana.getNetMana(game);
for (Mana m: manaAdded) {
if (m.getAny() > 0) {
return new ObjectColor("WUBRG");
}
if (m.getWhite() > 0) {
cl.setWhite(true);
}
if (m.getBlue() > 0) {
cl.setBlue(true);
}
if (m.getBlack() > 0) {
cl.setBlack(true);
}
if (m.getRed() > 0) {
cl.setRed(true);
}
if (m.getGreen() > 0) {
cl.setGreen(true);
}
}
} catch (NullPointerException e) {
// Ability depends on game
// but no game passed
// All such abilities are 5-color ones
return new ObjectColor("WUBRG");
}
}
}
return cl;
} else {
// For everything else, just return the frame colors
return frameColor;
}
}
@Override

View file

@ -50,7 +50,7 @@ public class MockCard extends CardImpl {
if (this.cardType.contains(CardType.PLANESWALKER)) {
String startingLoyaltyString = card.getStartingLoyalty();
if (startingLoyaltyString.isEmpty()) {
Logger.getLogger(MockCard.class).warn("Planeswalker `" + this.name + "` has empty starting loyalty.");
//Logger.getLogger(MockCard.class).warn("Planeswalker `" + this.name + "` has empty starting loyalty.");
} else {
try {
this.startingLoyalty = Integer.parseInt(startingLoyaltyString);

View file

@ -42,7 +42,6 @@ import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.PlanswalkerEntersWithLoyalityCountersAbility;
import mage.abilities.effects.PlaneswalkerRedirectionEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.mock.MockCard;
@ -190,7 +189,7 @@ public class CardInfo {
}
}
if (this.startingLoyalty == null) {
Logger.getLogger(CardInfo.class).warn("Planeswalker `" + card.getName() + "` missing starting loyalty");
//Logger.getLogger(CardInfo.class).warn("Planeswalker `" + card.getName() + "` missing starting loyalty");
this.startingLoyalty = "";
}
} else {

View file

@ -61,9 +61,9 @@ public enum CardRepository {
private static final String JDBC_URL = "jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE";
private static final String VERSION_ENTITY_NAME = "card";
// raise this if db structure was changed
private static final long CARD_DB_VERSION = 44;
private static final long CARD_DB_VERSION = 46;
// raise this if new cards were added to the server
private static final long CARD_CONTENT_VERSION = 55;
private static final long CARD_CONTENT_VERSION = 57;
private final Random random = new Random();
private Dao<CardInfo, Object> cardDao;

View file

@ -118,6 +118,7 @@ public class PermanentCard extends PermanentImpl {
this.cardType.clear();
this.cardType.addAll(card.getCardType());
this.color = card.getColor(null).copy();
this.frameColor = card.getFrameColor(null).copy();
this.manaCost = card.getManaCost().copy();
if (card instanceof PermanentCard) {
this.maxLevelCounters = ((PermanentCard) card).maxLevelCounters;

View file

@ -82,6 +82,7 @@ public class PermanentToken extends PermanentImpl {
}
this.cardType = token.getCardType();
this.color = token.getColor(game).copy();
this.frameColor = token.getFrameColor(game);
this.power.modifyBaseValue(token.getPower().getBaseValueModified());
this.toughness.modifyBaseValue(token.getToughness().getBaseValueModified());
this.supertype = token.getSupertype();