mirror of
https://github.com/correl/mage.git
synced 2024-12-26 19:16:54 +00:00
Merge pull request #20 from magefree/master
Merge https://github.com/magefree/mage
This commit is contained in:
commit
770af8d8e0
29 changed files with 32968 additions and 186 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -4,7 +4,7 @@ syntax: glob
|
||||||
Mage.Client/*.dck
|
Mage.Client/*.dck
|
||||||
Mage.Client/db
|
Mage.Client/db
|
||||||
Mage.Client/gamelogs
|
Mage.Client/gamelogs
|
||||||
Mage.Client/mageclient.log
|
Mage.Client/*.log
|
||||||
Mage.Client/plugins/images
|
Mage.Client/plugins/images
|
||||||
Mage.Client/plugins/plugin.data
|
Mage.Client/plugins/plugin.data
|
||||||
Mage.Client/target
|
Mage.Client/target
|
||||||
|
|
|
@ -562,7 +562,7 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTextOffset(int yOffset) {
|
public void setCardCaptionTopOffset(int yOffsetPercent) {
|
||||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ public class CardArea extends JPanel implements MouseListener {
|
||||||
private boolean reloaded = false;
|
private boolean reloaded = false;
|
||||||
private final javax.swing.JLayeredPane cardArea;
|
private final javax.swing.JLayeredPane cardArea;
|
||||||
private final javax.swing.JScrollPane scrollPane;
|
private final javax.swing.JScrollPane scrollPane;
|
||||||
private int yTextOffset;
|
private int yCardCaptionOffsetPercent = 0; // card caption offset (use for moving card caption view center, below mana icons -- for more good UI)
|
||||||
private Dimension cardDimension;
|
private Dimension cardDimension;
|
||||||
private int verticalCardOffset;
|
private int verticalCardOffset;
|
||||||
|
|
||||||
|
@ -68,8 +68,6 @@ public class CardArea extends JPanel implements MouseListener {
|
||||||
setGUISize();
|
setGUISize();
|
||||||
cardArea = new JLayeredPane();
|
cardArea = new JLayeredPane();
|
||||||
scrollPane.setViewportView(cardArea);
|
scrollPane.setViewportView(cardArea);
|
||||||
yTextOffset = 10;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanUp() {
|
public void cleanUp() {
|
||||||
|
@ -103,10 +101,10 @@ public class CardArea extends JPanel implements MouseListener {
|
||||||
this.reloaded = true;
|
this.reloaded = true;
|
||||||
cardArea.removeAll();
|
cardArea.removeAll();
|
||||||
if (showCards != null && showCards.size() < 10) {
|
if (showCards != null && showCards.size() < 10) {
|
||||||
yTextOffset = 10;
|
yCardCaptionOffsetPercent = 8; // TODO: need to test
|
||||||
loadCardsFew(showCards, bigCard, gameId);
|
loadCardsFew(showCards, bigCard, gameId);
|
||||||
} else {
|
} else {
|
||||||
yTextOffset = 0;
|
yCardCaptionOffsetPercent = 0;
|
||||||
loadCardsMany(showCards, bigCard, gameId);
|
loadCardsMany(showCards, bigCard, gameId);
|
||||||
}
|
}
|
||||||
cardArea.revalidate();
|
cardArea.revalidate();
|
||||||
|
@ -118,8 +116,10 @@ public class CardArea extends JPanel implements MouseListener {
|
||||||
public void loadCardsNarrow(CardsView showCards, BigCard bigCard, UUID gameId) {
|
public void loadCardsNarrow(CardsView showCards, BigCard bigCard, UUID gameId) {
|
||||||
this.reloaded = true;
|
this.reloaded = true;
|
||||||
cardArea.removeAll();
|
cardArea.removeAll();
|
||||||
yTextOffset = 0;
|
|
||||||
|
yCardCaptionOffsetPercent = 0; // TODO: need to test
|
||||||
loadCardsMany(showCards, bigCard, gameId);
|
loadCardsMany(showCards, bigCard, gameId);
|
||||||
|
|
||||||
cardArea.revalidate();
|
cardArea.revalidate();
|
||||||
|
|
||||||
this.revalidate();
|
this.revalidate();
|
||||||
|
@ -152,7 +152,10 @@ public class CardArea extends JPanel implements MouseListener {
|
||||||
cardArea.moveToFront(cardPanel);
|
cardArea.moveToFront(cardPanel);
|
||||||
cardPanel.update(card);
|
cardPanel.update(card);
|
||||||
cardPanel.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height);
|
cardPanel.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height);
|
||||||
cardPanel.setTextOffset(yTextOffset);
|
|
||||||
|
// new card have same settings as current view
|
||||||
|
cardPanel.setCardCaptionTopOffset(yCardCaptionOffsetPercent);
|
||||||
|
|
||||||
cardPanel.showCardTitle();
|
cardPanel.showCardTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1726,7 +1726,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
||||||
// Create the card view
|
// Create the card view
|
||||||
final MageCard cardPanel = Plugins.instance.getMageCard(card, lastBigCard, new Dimension(getCardWidth(), getCardHeight()), null, true, true);
|
final MageCard cardPanel = Plugins.instance.getMageCard(card, lastBigCard, new Dimension(getCardWidth(), getCardHeight()), null, true, true);
|
||||||
cardPanel.update(card);
|
cardPanel.update(card);
|
||||||
cardPanel.setTextOffset(0);
|
cardPanel.setCardCaptionTopOffset(0);
|
||||||
|
|
||||||
// Remove mouse wheel listeners so that scrolling works
|
// Remove mouse wheel listeners so that scrolling works
|
||||||
// Scrolling works on all areas without cards or by using the scroll bar, that's enough
|
// Scrolling works on all areas without cards or by using the scroll bar, that's enough
|
||||||
|
|
|
@ -236,6 +236,7 @@ public class MageBook extends JComponent {
|
||||||
for (int i = 0; i < Math.min(conf.CARDS_PER_PAGE / 2, size); i++) {
|
for (int i = 0; i < Math.min(conf.CARDS_PER_PAGE / 2, size); i++) {
|
||||||
Card card = cards.get(i).getMockCard();
|
Card card = cards.get(i).getMockCard();
|
||||||
addCard(new CardView(card), bigCard, null, rectangle);
|
addCard(new CardView(card), bigCard, null, rectangle);
|
||||||
|
|
||||||
rectangle = CardPosition.translatePosition(i, rectangle, conf);
|
rectangle = CardPosition.translatePosition(i, rectangle, conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,6 +342,8 @@ public class MageBook extends JComponent {
|
||||||
cardImg.update(card);
|
cardImg.update(card);
|
||||||
cardImg.setCardBounds(rectangle.x, rectangle.y, cardDimensions.frameWidth, cardDimensions.frameHeight);
|
cardImg.setCardBounds(rectangle.x, rectangle.y, cardDimensions.frameWidth, cardDimensions.frameHeight);
|
||||||
|
|
||||||
|
cardImg.setCardCaptionTopOffset(8); // card caption below real card caption to see full name even with mana icons
|
||||||
|
|
||||||
boolean implemented = card.getRarity() != Rarity.NA;
|
boolean implemented = card.getRarity() != Rarity.NA;
|
||||||
|
|
||||||
GlowText label = new GlowText();
|
GlowText label = new GlowText();
|
||||||
|
|
|
@ -91,7 +91,8 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
|
||||||
|
|
||||||
private JPanel cardArea;
|
private JPanel cardArea;
|
||||||
|
|
||||||
private int yTextOffset = 10;
|
// default offset, e.g. for battlefield
|
||||||
|
private int yCardCaptionOffsetPercent = 8; // card caption offset (use for moving card caption view center, below mana icons -- for more good UI)
|
||||||
|
|
||||||
// if this is set, it's opened if the user right clicks on the card panel
|
// if this is set, it's opened if the user right clicks on the card panel
|
||||||
private JPopupMenu popupMenu;
|
private JPopupMenu popupMenu;
|
||||||
|
@ -819,12 +820,12 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTextOffset(int yOffset) {
|
public void setCardCaptionTopOffset(int yOffsetPercent) {
|
||||||
yTextOffset = yOffset;
|
yCardCaptionOffsetPercent = yOffsetPercent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTextOffset() {
|
public int getCardCaptionTopOffset() {
|
||||||
return yTextOffset;
|
return yCardCaptionOffsetPercent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -34,7 +34,7 @@ import static org.mage.plugins.card.constants.Constants.THUMBNAIL_SIZE_FULL;
|
||||||
* Class for drawing the mage card object by using a form based JComponent
|
* Class for drawing the mage card object by using a form based JComponent
|
||||||
* approach
|
* approach
|
||||||
*
|
*
|
||||||
* @author arcane, nantuko, noxx, stravant
|
* @author arcane, nantuko, noxx, stravant, JayDi85
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
public class CardPanelComponentImpl extends CardPanel {
|
public class CardPanelComponentImpl extends CardPanel {
|
||||||
|
@ -47,9 +47,14 @@ public class CardPanelComponentImpl extends CardPanel {
|
||||||
|
|
||||||
private static final float ROUNDED_CORNER_SIZE = 0.1f;
|
private static final float ROUNDED_CORNER_SIZE = 0.1f;
|
||||||
private static final float BLACK_BORDER_SIZE = 0.03f;
|
private static final float BLACK_BORDER_SIZE = 0.03f;
|
||||||
|
private static final float SELECTION_BORDER_SIZE = 0.03f;
|
||||||
private static final int TEXT_GLOW_SIZE = 6;
|
private static final int TEXT_GLOW_SIZE = 6;
|
||||||
private static final float TEXT_GLOW_INTENSITY = 3f;
|
private static final float TEXT_GLOW_INTENSITY = 3f;
|
||||||
|
|
||||||
|
// size to show icons and text (help to see full size card without text)
|
||||||
|
private static final int CARD_MIN_SIZE_FOR_ICONS = 60;
|
||||||
|
private static final int CARD_MAX_SIZE_FOR_ICONS = 200;
|
||||||
|
|
||||||
public final ScaledImagePanel imagePanel;
|
public final ScaledImagePanel imagePanel;
|
||||||
public ImagePanel overlayPanel;
|
public ImagePanel overlayPanel;
|
||||||
|
|
||||||
|
@ -177,6 +182,34 @@ public class CardPanelComponentImpl extends CardPanel {
|
||||||
IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap((Function<Key, BufferedImage>) key -> createImage(key)));
|
IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap((Function<Key, BufferedImage>) key -> createImage(key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static private boolean canShowCardIcons(int cardFullWidth, boolean cardHasImage){
|
||||||
|
// cards without images show icons and text always
|
||||||
|
// TODO: apply "card names on card" setting to icon too?
|
||||||
|
// TODO: fix card min-max size to hide (compare to settings size, not direct 60 and 200)
|
||||||
|
return ((cardFullWidth > 60) && (cardFullWidth < 200)) || (!cardHasImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CardSizes{
|
||||||
|
Rectangle rectFull;
|
||||||
|
Rectangle rectSelection;
|
||||||
|
Rectangle rectBorder;
|
||||||
|
Rectangle rectCard;
|
||||||
|
|
||||||
|
CardSizes(int offsetX, int offsetY, int fullWidth, int fullHeight){
|
||||||
|
|
||||||
|
int realBorderSizeX = Math.round(fullWidth * BLACK_BORDER_SIZE);
|
||||||
|
int realBorderSizeY = Math.round(fullWidth * BLACK_BORDER_SIZE);
|
||||||
|
int realSelectionSizeX = Math.round(fullWidth * SELECTION_BORDER_SIZE);
|
||||||
|
int realSelectionSizeY = Math.round(fullWidth * SELECTION_BORDER_SIZE);
|
||||||
|
|
||||||
|
// card full size = select border + black border + real card
|
||||||
|
rectFull = new Rectangle(offsetX, offsetY, fullWidth, fullHeight);
|
||||||
|
rectSelection = new Rectangle(rectFull.x, rectFull.y, rectFull.width, rectFull.height);
|
||||||
|
rectBorder = new Rectangle(rectSelection.x + realSelectionSizeX, rectSelection.y + realSelectionSizeY, rectSelection.width - 2 * realSelectionSizeX, rectSelection.height - 2 * realSelectionSizeY);
|
||||||
|
rectCard = new Rectangle(rectBorder.x + realBorderSizeX, rectBorder.y + realBorderSizeY, rectBorder.width - 2 * realBorderSizeX, rectBorder.height - 2 * realBorderSizeY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public CardPanelComponentImpl(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension) {
|
public CardPanelComponentImpl(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension) {
|
||||||
// Call to super
|
// Call to super
|
||||||
super(newGameCard, gameId, loadImage, callback, foil, dimension);
|
super(newGameCard, gameId, loadImage, callback, foil, dimension);
|
||||||
|
@ -368,32 +401,45 @@ public class CardPanelComponentImpl extends CardPanel {
|
||||||
Graphics2D g2d = image.createGraphics();
|
Graphics2D g2d = image.createGraphics();
|
||||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
if (!key.hasImage) {
|
// card full size = select border + black border + real card
|
||||||
g2d.setColor(new Color(30, 200, 200, 120));
|
CardSizes sizes = new CardSizes(cardXOffset, cardYOffset, cardWidth, cardHeight);
|
||||||
} else {
|
|
||||||
g2d.setColor(new Color(0, 0, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
int cornerSize = Math.max(4, Math.round(cardWidth * ROUNDED_CORNER_SIZE));
|
// corners for selection and for border
|
||||||
g2d.fillRoundRect(cardXOffset, cardYOffset, cardWidth, cardHeight, cornerSize, cornerSize);
|
int cornerSizeSelection = Math.max(4, Math.round(sizes.rectSelection.width * ROUNDED_CORNER_SIZE));
|
||||||
|
int cornerSizeBorder = Math.max(4, Math.round(sizes.rectBorder.width * ROUNDED_CORNER_SIZE));
|
||||||
|
|
||||||
|
// DRAW ORDER from big to small: select -> select info -> border -> card
|
||||||
|
|
||||||
|
// draw selection
|
||||||
if (key.isSelected) {
|
if (key.isSelected) {
|
||||||
g2d.setColor(Color.green);
|
g2d.setColor(Color.green);
|
||||||
g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize);
|
g2d.fillRoundRect(sizes.rectSelection.x + 1, sizes.rectSelection.y + 1, sizes.rectSelection.width - 2, sizes.rectSelection.height - 2, cornerSizeSelection, cornerSizeSelection);
|
||||||
} else if (key.isChoosable) {
|
} else if (key.isChoosable) {
|
||||||
g2d.setColor(new Color(250, 250, 0, 230));
|
g2d.setColor(new Color(250, 250, 0, 230));
|
||||||
g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize);
|
g2d.fillRoundRect(sizes.rectSelection.x + 1, sizes.rectSelection.y + 1, sizes.rectSelection.width - 2, sizes.rectSelection.height - 2, cornerSizeSelection, cornerSizeSelection);
|
||||||
} else if (key.isPlayable) {
|
} else if (key.isPlayable) {
|
||||||
g2d.setColor(new Color(153, 102, 204, 200));
|
g2d.setColor(new Color(153, 102, 204, 200));
|
||||||
//g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize);
|
g2d.fillRoundRect(sizes.rectSelection.x, sizes.rectSelection.y, sizes.rectSelection.width, sizes.rectSelection.height, cornerSizeSelection, cornerSizeSelection);
|
||||||
g2d.fillRoundRect(cardXOffset, cardYOffset, cardWidth, cardHeight, cornerSize, cornerSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// draw empty card with border
|
||||||
|
if (!key.hasImage) {
|
||||||
|
// gray 1 px border
|
||||||
|
g2d.setColor(new Color(125, 125, 125, 255));
|
||||||
|
g2d.fillRoundRect(sizes.rectBorder.x, sizes.rectBorder.y, sizes.rectBorder.width, sizes.rectBorder.height, cornerSizeBorder, cornerSizeBorder);
|
||||||
|
// color plate
|
||||||
|
g2d.setColor(new Color(30, 200, 200, 200));
|
||||||
|
g2d.fillRoundRect(sizes.rectBorder.x + 1, sizes.rectBorder.y + 1, sizes.rectBorder.width - 2, sizes.rectBorder.height - 2, cornerSizeBorder, cornerSizeBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw attack border (inner part of selection)
|
||||||
if (key.canAttack) {
|
if (key.canAttack) {
|
||||||
g2d.setColor(new Color(0, 0, 255, 230));
|
g2d.setColor(new Color(0, 0, 255, 230));
|
||||||
g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize);
|
g2d.fillRoundRect(sizes.rectBorder.x + 1, sizes.rectBorder.y + 1, sizes.rectBorder.width - 2, sizes.rectBorder.height - 2, cornerSizeBorder, cornerSizeBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// draw real card by component (see imagePanel and other layout's items)
|
||||||
|
|
||||||
//TODO:uncomment
|
//TODO:uncomment
|
||||||
/*
|
/*
|
||||||
if (gameCard.isAttacking()) {
|
if (gameCard.isAttacking()) {
|
||||||
|
@ -409,18 +455,54 @@ public class CardPanelComponentImpl extends CardPanel {
|
||||||
protected void paintChildren(Graphics g) {
|
protected void paintChildren(Graphics g) {
|
||||||
super.paintChildren(g);
|
super.paintChildren(g);
|
||||||
|
|
||||||
if (getShowCastingCost() && !isAnimationPanel() && getCardWidth() < 200 && getCardWidth() > 60) {
|
CardSizes realCard = new CardSizes(getCardXOffset(), getCardYOffset(), getCardWidth(), getCardHeight());
|
||||||
|
|
||||||
|
/*
|
||||||
|
// draw recs for debug
|
||||||
|
|
||||||
|
// full card
|
||||||
|
g.setColor(new Color(255, 0, 0));
|
||||||
|
g.drawRect(realCard.rectFull.x, realCard.rectFull.y, realCard.rectFull.width, realCard.rectFull.height);
|
||||||
|
|
||||||
|
// real card - image
|
||||||
|
g.setColor(new Color(0, 0, 255));
|
||||||
|
g.drawRect(imagePanel.getX(), imagePanel.getY(), imagePanel.getBounds().width, imagePanel.getBounds().height);
|
||||||
|
|
||||||
|
// caption
|
||||||
|
g.setColor(new Color(0, 255, 255));
|
||||||
|
g.drawRect(titleText.getX(), titleText.getY(), titleText.getBounds().width, titleText.getBounds().height);
|
||||||
|
|
||||||
|
// life points
|
||||||
|
g.setColor(new Color(120, 0, 120));
|
||||||
|
g.drawRect(ptText.getX(), ptText.getY(), ptText.getBounds().width, ptText.getBounds().height);
|
||||||
|
//*/
|
||||||
|
|
||||||
|
if (getShowCastingCost() && !isAnimationPanel() && canShowCardIcons(getCardWidth(), hasImage)) {
|
||||||
|
|
||||||
String manaCost = ManaSymbols.getStringManaCost(gameCard.getManaCost());
|
String manaCost = ManaSymbols.getStringManaCost(gameCard.getManaCost());
|
||||||
int width = getWidth(manaCost);
|
int manaWidth = getManaWidth(manaCost);
|
||||||
|
|
||||||
|
// right top corner with margin (sizes from any sample card, length from black border to mana icon)
|
||||||
|
int manaMarginRight = Math.round(22f / 672f * getCardWidth());
|
||||||
|
int manaMarginTop = Math.round(24f / 936f * getCardHeight());
|
||||||
|
|
||||||
|
int manaX = getCardXOffset() + getCardWidth() - manaMarginRight - manaWidth;
|
||||||
|
int manaY = getCardYOffset() + manaMarginTop;
|
||||||
|
|
||||||
if (hasImage) {
|
if (hasImage) {
|
||||||
ManaSymbols.draw(g, manaCost, getCardXOffset() + getCardWidth() - width - 5, getCardYOffset() + 5, getSymbolWidth());
|
// top right corner if have image like real card
|
||||||
|
ManaSymbols.draw(g, manaCost, manaX, manaY, getSymbolWidth());
|
||||||
} else {
|
} else {
|
||||||
ManaSymbols.draw(g, manaCost, getCardXOffset() + 8, getCardHeight() - 9, getSymbolWidth());
|
// old version - bottom left corner if haven't card
|
||||||
|
// ManaSymbols.draw(g, manaCost, getCardXOffset() + manaMarginRight, getCardYOffset() + getCardHeight() - manaMarginTop - getSymbolWidth(), getSymbolWidth());
|
||||||
|
|
||||||
|
// new version - like a normal image (it's best to view and construct decks)
|
||||||
|
ManaSymbols.draw(g, manaCost, manaX, manaY, getSymbolWidth());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getWidth(String manaCost) {
|
private int getManaWidth(String manaCost) {
|
||||||
int width = 0;
|
int width = 0;
|
||||||
manaCost = manaCost.replace("\\", "");
|
manaCost = manaCost.replace("\\", "");
|
||||||
StringTokenizer tok = new StringTokenizer(manaCost, " ");
|
StringTokenizer tok = new StringTokenizer(manaCost, " ");
|
||||||
|
@ -439,24 +521,28 @@ public class CardPanelComponentImpl extends CardPanel {
|
||||||
int cardHeight = getCardHeight();
|
int cardHeight = getCardHeight();
|
||||||
int cardXOffset = getCardXOffset();
|
int cardXOffset = getCardXOffset();
|
||||||
int cardYOffset = getCardYOffset();
|
int cardYOffset = getCardYOffset();
|
||||||
int borderSize = Math.round(cardWidth * BLACK_BORDER_SIZE);
|
|
||||||
imagePanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize);
|
CardSizes sizes = new CardSizes(cardXOffset, cardYOffset, cardWidth, cardHeight);
|
||||||
imagePanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2);
|
|
||||||
|
// origin card without selection
|
||||||
|
Rectangle realCardSize = sizes.rectBorder;
|
||||||
|
imagePanel.setLocation(realCardSize.x, realCardSize.y);
|
||||||
|
imagePanel.setSize(realCardSize.width, realCardSize.height);
|
||||||
|
|
||||||
if (hasSickness() && gameCard.isCreature() && isPermanent()) {
|
if (hasSickness() && gameCard.isCreature() && isPermanent()) {
|
||||||
overlayPanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize);
|
overlayPanel.setLocation(realCardSize.x, realCardSize.y);
|
||||||
overlayPanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2);
|
overlayPanel.setSize(realCardSize.width, realCardSize.height);
|
||||||
} else {
|
} else {
|
||||||
overlayPanel.setVisible(false);
|
overlayPanel.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iconPanel != null) {
|
if (iconPanel != null) {
|
||||||
iconPanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize);
|
iconPanel.setLocation(realCardSize.x, realCardSize.y);
|
||||||
iconPanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2);
|
iconPanel.setSize(realCardSize.width, realCardSize.height);
|
||||||
}
|
}
|
||||||
if (counterPanel != null) {
|
if (counterPanel != null) {
|
||||||
counterPanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize);
|
counterPanel.setLocation(realCardSize.x, realCardSize.y);
|
||||||
counterPanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2);
|
counterPanel.setSize(realCardSize.width, realCardSize.height);
|
||||||
int size = cardWidth > WIDTH_LIMIT ? 40 : 20;
|
int size = cardWidth > WIDTH_LIMIT ? 40 : 20;
|
||||||
|
|
||||||
minusCounterLabel.setLocation(counterPanel.getWidth() - size, counterPanel.getHeight() - size * 2);
|
minusCounterLabel.setLocation(counterPanel.getWidth() - size, counterPanel.getHeight() - size * 2);
|
||||||
|
@ -472,32 +558,52 @@ public class CardPanelComponentImpl extends CardPanel {
|
||||||
otherCounterLabel.setSize(size, size);
|
otherCounterLabel.setSize(size, size);
|
||||||
|
|
||||||
}
|
}
|
||||||
int fontHeight = Math.round(cardHeight * (27f / 680));
|
|
||||||
boolean showText = (!isAnimationPanel() && fontHeight < 12);
|
// TITLE
|
||||||
|
|
||||||
|
//old version - text hide on small fonts, why?
|
||||||
|
//int fontHeight = Math.round(cardHeight * (26f / 672));
|
||||||
|
//boolean showText = (!isAnimationPanel() && fontHeight < 12);
|
||||||
|
|
||||||
|
boolean showText = !isAnimationPanel() && canShowCardIcons(cardWidth, hasImage);
|
||||||
titleText.setVisible(showText);
|
titleText.setVisible(showText);
|
||||||
ptText.setVisible(showText);
|
ptText.setVisible(showText);
|
||||||
fullImageText.setVisible(fullImagePath != null);
|
fullImageText.setVisible(fullImagePath != null);
|
||||||
|
|
||||||
if (showText) {
|
if (showText) {
|
||||||
int fontSize = cardHeight / 11;
|
int fontSize = cardHeight / 13; // startup font size (it same size on all zoom levels)
|
||||||
titleText.setFont(getFont().deriveFont(Font.BOLD, fontSize));
|
titleText.setFont(getFont().deriveFont(Font.BOLD, fontSize));
|
||||||
|
|
||||||
int titleX = Math.round(cardWidth * (20f / 480));
|
// margins from card black border to text, not need? text show up good without margins
|
||||||
int titleY = Math.round(cardHeight * (9f / 680)) + getTextOffset();
|
int titleMarginLeft = 0; //Math.round(28f / 672f * cardWidth);
|
||||||
titleText.setBounds(cardXOffset + titleX, cardYOffset + titleY, cardWidth - titleX, cardHeight - titleY);
|
int titleMarginRight = 0;
|
||||||
|
int titleMarginTop = 0 + Math.round(getCardCaptionTopOffset() / 100f * cardHeight);//Math.round(28f / 936f * cardHeight);
|
||||||
|
int titleMarginBottom = 0;
|
||||||
|
titleText.setBounds(
|
||||||
|
imagePanel.getX() + titleMarginLeft,
|
||||||
|
imagePanel.getY() + titleMarginTop,
|
||||||
|
imagePanel.getBounds().width - titleMarginLeft - titleMarginRight,
|
||||||
|
imagePanel.getBounds().height - titleMarginTop - titleMarginBottom
|
||||||
|
);
|
||||||
|
|
||||||
fullImageText.setFont(getFont().deriveFont(Font.PLAIN, 10));
|
fullImageText.setFont(getFont().deriveFont(Font.PLAIN, 10));
|
||||||
fullImageText.setBounds(cardXOffset, cardYOffset + titleY, cardWidth, cardHeight - titleY);
|
fullImageText.setBounds(titleText.getX(), titleText.getY(), titleText.getBounds().width, titleText.getBounds().height);
|
||||||
|
|
||||||
|
// life points location (font as title)
|
||||||
ptText.setFont(getFont().deriveFont(Font.BOLD, fontSize));
|
ptText.setFont(getFont().deriveFont(Font.BOLD, fontSize));
|
||||||
Dimension ptSize = ptText.getPreferredSize();
|
Dimension ptSize = ptText.getPreferredSize();
|
||||||
ptText.setSize(ptSize.width, ptSize.height);
|
ptText.setSize(ptSize.width, ptSize.height);
|
||||||
int ptX = Math.round(cardWidth * (420f / 480)) - ptSize.width / 2;
|
|
||||||
int ptY = Math.round(cardHeight * (675f / 680)) - ptSize.height;
|
|
||||||
|
|
||||||
int offsetX = Math.round((CARD_SIZE_FULL.width - cardWidth) / 10.0f);
|
// right bottom corner with margin (sizes from any sample card)
|
||||||
|
int ptMarginRight = Math.round(64f / 672f * cardWidth);
|
||||||
|
int ptMarginBottom = Math.round(62f / 936f * cardHeight);
|
||||||
|
|
||||||
ptText.setLocation(cardXOffset + ptX - TEXT_GLOW_SIZE / 2 - offsetX, cardYOffset + ptY - TEXT_GLOW_SIZE / 2);
|
int ptX = cardXOffset + cardWidth - ptMarginRight - ptSize.width;
|
||||||
|
int ptY = cardYOffset + cardHeight - ptMarginBottom - ptSize.height;
|
||||||
|
ptText.setLocation(ptX, ptY);
|
||||||
|
|
||||||
|
// old version was with TEXT_GLOW_SIZE
|
||||||
|
//ptText.setLocation(cardXOffset + ptX - TEXT_GLOW_SIZE / 2 - offsetX, cardYOffset + ptY - TEXT_GLOW_SIZE / 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -581,7 +687,7 @@ public class CardPanelComponentImpl extends CardPanel {
|
||||||
} else if (this.gameCard instanceof StackAbilityView) {
|
} else if (this.gameCard instanceof StackAbilityView) {
|
||||||
return ImageCache.getMorphImage();
|
return ImageCache.getMorphImage();
|
||||||
} else {
|
} else {
|
||||||
return ImageCache.loadImage(new TFile(DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename));
|
return ImageCache.getCardbackImage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -218,13 +218,15 @@ public class CardPanelRenderImpl extends CardPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Map of generated images
|
// Map of generated images
|
||||||
private final static Map<ImageKey, BufferedImage> IMAGE_CACHE = new MapMaker().softValues().makeMap();
|
private final static Map<ImageKey, BufferedImage> IMAGE_CACHE = new MapMaker().softValues().makeMap();
|
||||||
|
|
||||||
// The art image for the card, loaded in from the disk
|
// The art image for the card, loaded in from the disk
|
||||||
private BufferedImage artImage;
|
private BufferedImage artImage;
|
||||||
|
|
||||||
|
// The faceart image for the card, loaded in from the disk (based on artid from mtgo)
|
||||||
|
private BufferedImage faceArtImage;
|
||||||
|
|
||||||
// Factory to generate card appropriate views
|
// Factory to generate card appropriate views
|
||||||
private CardRendererFactory cardRendererFactory = new CardRendererFactory();
|
private CardRendererFactory cardRendererFactory = new CardRendererFactory();
|
||||||
|
|
||||||
|
@ -252,6 +254,8 @@ public class CardPanelRenderImpl extends CardPanel {
|
||||||
// Use the art image and current rendered image from the card
|
// Use the art image and current rendered image from the card
|
||||||
artImage = impl.artImage;
|
artImage = impl.artImage;
|
||||||
cardRenderer.setArtImage(artImage);
|
cardRenderer.setArtImage(artImage);
|
||||||
|
faceArtImage = impl.faceArtImage;
|
||||||
|
cardRenderer.setFaceArtImage(faceArtImage);
|
||||||
cardImage = impl.cardImage;
|
cardImage = impl.cardImage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,7 +281,6 @@ public class CardPanelRenderImpl extends CardPanel {
|
||||||
/**
|
/**
|
||||||
* Create an appropriate card renderer for the
|
* Create an appropriate card renderer for the
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the card to a new BufferedImage at it's current dimensions
|
* Render the card to a new BufferedImage at it's current dimensions
|
||||||
*
|
*
|
||||||
|
@ -315,6 +318,7 @@ public class CardPanelRenderImpl extends CardPanel {
|
||||||
artImage = null;
|
artImage = null;
|
||||||
cardImage = null;
|
cardImage = null;
|
||||||
cardRenderer.setArtImage(null);
|
cardRenderer.setArtImage(null);
|
||||||
|
cardRenderer.setFaceArtImage(null);
|
||||||
|
|
||||||
// Stop animation
|
// Stop animation
|
||||||
tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0;
|
tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0;
|
||||||
|
@ -332,19 +336,26 @@ public class CardPanelRenderImpl extends CardPanel {
|
||||||
Util.threadPool.submit(() -> {
|
Util.threadPool.submit(() -> {
|
||||||
try {
|
try {
|
||||||
final BufferedImage srcImage;
|
final BufferedImage srcImage;
|
||||||
|
final BufferedImage faceArtSrcImage;
|
||||||
if (gameCard.isFaceDown()) {
|
if (gameCard.isFaceDown()) {
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
srcImage = null;
|
srcImage = null;
|
||||||
|
faceArtSrcImage = null;
|
||||||
} else if (getCardWidth() > THUMBNAIL_SIZE_FULL.width) {
|
} else if (getCardWidth() > THUMBNAIL_SIZE_FULL.width) {
|
||||||
srcImage = ImageCache.getImage(gameCard, getCardWidth(), getCardHeight());
|
srcImage = ImageCache.getImage(gameCard, getCardWidth(), getCardHeight());
|
||||||
|
faceArtSrcImage = ImageCache.getFaceImage(gameCard, getCardWidth(), getCardHeight());
|
||||||
} else {
|
} else {
|
||||||
srcImage = ImageCache.getThumbnail(gameCard);
|
srcImage = ImageCache.getThumbnail(gameCard);
|
||||||
|
faceArtSrcImage = ImageCache.getFaceImage(gameCard, getCardWidth(), getCardHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
UI.invokeLater(() -> {
|
UI.invokeLater(() -> {
|
||||||
if (stamp == updateArtImageStamp) {
|
if (stamp == updateArtImageStamp) {
|
||||||
artImage = srcImage;
|
artImage = srcImage;
|
||||||
cardRenderer.setArtImage(srcImage);
|
cardRenderer.setArtImage(srcImage);
|
||||||
|
faceArtImage = faceArtSrcImage;
|
||||||
|
cardRenderer.setFaceArtImage(faceArtSrcImage);
|
||||||
|
|
||||||
if (srcImage != null) {
|
if (srcImage != null) {
|
||||||
// Invalidate and repaint
|
// Invalidate and repaint
|
||||||
cardImage = null;
|
cardImage = null;
|
||||||
|
@ -370,6 +381,7 @@ public class CardPanelRenderImpl extends CardPanel {
|
||||||
cardImage = null;
|
cardImage = null;
|
||||||
cardRenderer = cardRendererFactory.create(gameCard, isTransformed());
|
cardRenderer = cardRendererFactory.create(gameCard, isTransformed());
|
||||||
cardRenderer.setArtImage(artImage);
|
cardRenderer.setArtImage(artImage);
|
||||||
|
cardRenderer.setFaceArtImage(faceArtImage);
|
||||||
|
|
||||||
// Repaint
|
// Repaint
|
||||||
repaint();
|
repaint();
|
||||||
|
@ -398,7 +410,7 @@ public class CardPanelRenderImpl extends CardPanel {
|
||||||
} else if (this.gameCard instanceof StackAbilityView) {
|
} else if (this.gameCard instanceof StackAbilityView) {
|
||||||
return ImageCache.getMorphImage();
|
return ImageCache.getMorphImage();
|
||||||
} else {
|
} else {
|
||||||
return ImageCache.loadImage(new TFile(DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename));
|
return ImageCache.getCardbackImage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,9 @@ public abstract class CardRenderer {
|
||||||
// The card image
|
// The card image
|
||||||
protected BufferedImage artImage;
|
protected BufferedImage artImage;
|
||||||
|
|
||||||
|
// The face card image
|
||||||
|
protected BufferedImage faceArtImage;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Common layout metrics between all cards
|
// Common layout metrics between all cards
|
||||||
// Polygons for counters
|
// Polygons for counters
|
||||||
|
@ -300,6 +303,44 @@ public abstract class CardRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void drawFaceArtIntoRect(Graphics2D g, int x, int y, int w, int h, Rectangle2D artRect, boolean shouldPreserveAspect) {
|
||||||
|
// Perform a process to make sure that the art is scaled uniformly to fill the frame, cutting
|
||||||
|
// off the minimum amount necessary to make it completely fill the frame without "squashing" it.
|
||||||
|
double fullCardImgWidth = faceArtImage.getWidth();
|
||||||
|
double fullCardImgHeight = faceArtImage.getHeight();
|
||||||
|
double artWidth = fullCardImgWidth;
|
||||||
|
double artHeight = fullCardImgHeight;
|
||||||
|
double targetWidth = w;
|
||||||
|
double targetHeight = h;
|
||||||
|
double targetAspect = targetWidth / targetHeight;
|
||||||
|
if (!shouldPreserveAspect) {
|
||||||
|
// No adjustment to art
|
||||||
|
} else if (targetAspect * artHeight < artWidth) {
|
||||||
|
// Trim off some width
|
||||||
|
artWidth = targetAspect * artHeight;
|
||||||
|
} else {
|
||||||
|
// Trim off some height
|
||||||
|
artHeight = artWidth / targetAspect;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
/*BufferedImage subImg
|
||||||
|
= faceArtImage.getSubimage(
|
||||||
|
(int) (artRect.getX() * fullCardImgWidth), (int) (artRect.getY() * fullCardImgHeight),
|
||||||
|
(int) artWidth, (int) artHeight);*/
|
||||||
|
RenderingHints rh = new RenderingHints(
|
||||||
|
RenderingHints.KEY_INTERPOLATION,
|
||||||
|
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||||
|
g.setRenderingHints(rh);
|
||||||
|
g.drawImage(faceArtImage,
|
||||||
|
x, y,
|
||||||
|
(int) targetWidth, (int) targetHeight,
|
||||||
|
null);
|
||||||
|
} catch (RasterFormatException e) {
|
||||||
|
// At very small card sizes we may encounter a problem with rounding error making the rect not fit
|
||||||
|
System.out.println(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Draw +1/+1 and other counters
|
// Draw +1/+1 and other counters
|
||||||
protected void drawCounters(Graphics2D g) {
|
protected void drawCounters(Graphics2D g) {
|
||||||
int xPos = (int) (0.65 * cardWidth);
|
int xPos = (int) (0.65 * cardWidth);
|
||||||
|
@ -442,4 +483,10 @@ public abstract class CardRenderer {
|
||||||
public void setArtImage(Image image) {
|
public void setArtImage(Image image) {
|
||||||
artImage = CardRendererUtils.toBufferedImage(image);
|
artImage = CardRendererUtils.toBufferedImage(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the card art image (CardPanel will give it to us when it
|
||||||
|
// is loaded and ready)
|
||||||
|
public void setFaceArtImage(Image image) {
|
||||||
|
faceArtImage = CardRendererUtils.toBufferedImage(image);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,6 +279,8 @@ public class ModernCardRenderer extends CardRenderer {
|
||||||
// Just draw a brown rectangle
|
// Just draw a brown rectangle
|
||||||
drawCardBack(g);
|
drawCardBack(g);
|
||||||
} else {
|
} else {
|
||||||
|
BufferedImage bufferedImage = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
|
||||||
|
|
||||||
// Set texture to paint with
|
// Set texture to paint with
|
||||||
g.setPaint(getBackgroundPaint(cardView.getColor(), cardView.getCardTypes(), cardView.getSubTypes()));
|
g.setPaint(getBackgroundPaint(cardView.getColor(), cardView.getCardTypes(), cardView.getSubTypes()));
|
||||||
|
|
||||||
|
@ -348,8 +350,15 @@ public class ModernCardRenderer extends CardRenderer {
|
||||||
@Override
|
@Override
|
||||||
protected void drawArt(Graphics2D g) {
|
protected void drawArt(Graphics2D g) {
|
||||||
if (artImage != null && !cardView.isFaceDown()) {
|
if (artImage != null && !cardView.isFaceDown()) {
|
||||||
|
|
||||||
|
boolean useFaceArt = false;
|
||||||
|
if (faceArtImage != null) {
|
||||||
|
useFaceArt = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Invention rendering, art fills the entire frame
|
// Invention rendering, art fills the entire frame
|
||||||
if (useInventionFrame()) {
|
if (useInventionFrame()) {
|
||||||
|
useFaceArt = false;
|
||||||
drawArtIntoRect(g,
|
drawArtIntoRect(g,
|
||||||
borderWidth, borderWidth,
|
borderWidth, borderWidth,
|
||||||
cardWidth - 2 * borderWidth, cardHeight - 2 * borderWidth,
|
cardWidth - 2 * borderWidth, cardHeight - 2 * borderWidth,
|
||||||
|
@ -360,6 +369,7 @@ public class ModernCardRenderer extends CardRenderer {
|
||||||
Rectangle2D sourceRect = getArtRect();
|
Rectangle2D sourceRect = getArtRect();
|
||||||
|
|
||||||
if (cardView.getMageObjectType() == MageObjectType.SPELL) {
|
if (cardView.getMageObjectType() == MageObjectType.SPELL) {
|
||||||
|
useFaceArt = false;
|
||||||
ArtRect rect = cardView.getArtRect();
|
ArtRect rect = cardView.getArtRect();
|
||||||
if (rect == ArtRect.SPLIT_FUSED) {
|
if (rect == ArtRect.SPLIT_FUSED) {
|
||||||
// Special handling for fused, draw the art from both halves stacked on top of one and other
|
// Special handling for fused, draw the art from both halves stacked on top of one and other
|
||||||
|
@ -380,12 +390,19 @@ public class ModernCardRenderer extends CardRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal drawing of art from a source part of the card frame into the rect
|
// Normal drawing of art from a source part of the card frame into the rect
|
||||||
|
if (useFaceArt) {
|
||||||
|
drawFaceArtIntoRect(g,
|
||||||
|
totalContentInset + 1, totalContentInset + boxHeight,
|
||||||
|
contentWidth - 2, typeLineY - totalContentInset - boxHeight,
|
||||||
|
sourceRect, shouldPreserveAspect);
|
||||||
|
} else {
|
||||||
drawArtIntoRect(g,
|
drawArtIntoRect(g,
|
||||||
totalContentInset + 1, totalContentInset + boxHeight,
|
totalContentInset + 1, totalContentInset + boxHeight,
|
||||||
contentWidth - 2, typeLineY - totalContentInset - boxHeight,
|
contentWidth - 2, typeLineY - totalContentInset - boxHeight,
|
||||||
sourceRect, shouldPreserveAspect);
|
sourceRect, shouldPreserveAspect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void drawFrame(Graphics2D g) {
|
protected void drawFrame(Graphics2D g) {
|
||||||
|
@ -420,6 +437,7 @@ public class ModernCardRenderer extends CardRenderer {
|
||||||
g.setPaint(new Color(255, 255, 255, 150));
|
g.setPaint(new Color(255, 255, 255, 150));
|
||||||
} else {
|
} else {
|
||||||
g.setPaint(textboxPaint);
|
g.setPaint(textboxPaint);
|
||||||
|
|
||||||
}
|
}
|
||||||
g.fillRect(
|
g.fillRect(
|
||||||
totalContentInset + 1, typeLineY,
|
totalContentInset + 1, typeLineY,
|
||||||
|
@ -476,6 +494,9 @@ public class ModernCardRenderer extends CardRenderer {
|
||||||
// Draw the transform circle
|
// Draw the transform circle
|
||||||
int nameOffset = drawTransformationCircle(g, borderPaint);
|
int nameOffset = drawTransformationCircle(g, borderPaint);
|
||||||
|
|
||||||
|
// Draw the transform circle
|
||||||
|
nameOffset = drawTransformationCircle(g, borderPaint);
|
||||||
|
|
||||||
// Draw the name line
|
// Draw the name line
|
||||||
drawNameLine(g, cardView.getDisplayName(), manaCostString,
|
drawNameLine(g, cardView.getDisplayName(), manaCostString,
|
||||||
totalContentInset + nameOffset, totalContentInset,
|
totalContentInset + nameOffset, totalContentInset,
|
||||||
|
|
|
@ -22,7 +22,7 @@ import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
||||||
*/
|
*/
|
||||||
public class DirectLinksForDownload implements Iterable<DownloadJob> {
|
public class DirectLinksForDownload implements Iterable<DownloadJob> {
|
||||||
|
|
||||||
private static final String backsideUrl = "http://upload.wikimedia.org/wikipedia/en/a/aa/Magic_the_gathering-card_back.jpg";
|
private static final String backsideUrl = "https://upload.wikimedia.org/wikipedia/en/a/aa/Magic_the_gathering-card_back.jpg";
|
||||||
|
|
||||||
private static final Map<String, String> directLinks = new LinkedHashMap<>();
|
private static final Map<String, String> directLinks = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
|
|
@ -6,19 +6,44 @@ import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import mage.cards.ExpansionSet;
|
import mage.cards.ExpansionSet;
|
||||||
import mage.cards.Sets;
|
import mage.cards.Sets;
|
||||||
|
import mage.constants.Rarity;
|
||||||
import org.mage.plugins.card.dl.DownloadJob;
|
import org.mage.plugins.card.dl.DownloadJob;
|
||||||
|
|
||||||
import static org.mage.plugins.card.dl.DownloadJob.fromURL;
|
import static org.mage.plugins.card.dl.DownloadJob.fromURL;
|
||||||
import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
public class GathererSets implements Iterable<DownloadJob> {
|
public class GathererSets implements Iterable<DownloadJob> {
|
||||||
|
|
||||||
|
private class CheckResult {
|
||||||
|
String code;
|
||||||
|
ExpansionSet set;
|
||||||
|
boolean haveCommon;
|
||||||
|
boolean haveUncommon;
|
||||||
|
boolean haveRare;
|
||||||
|
boolean haveMyth;
|
||||||
|
|
||||||
|
private CheckResult(String ACode, ExpansionSet ASet, boolean AHaveCommon, boolean AHaveUncommon, boolean AHhaveRare, boolean AHaveMyth) {
|
||||||
|
code = ACode;
|
||||||
|
set = ASet;
|
||||||
|
haveCommon = AHaveCommon;
|
||||||
|
haveUncommon = AHaveUncommon;
|
||||||
|
haveRare = AHhaveRare;
|
||||||
|
haveMyth = AHaveMyth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int DAYS_BEFORE_RELEASE_TO_DOWNLOAD = +14; // Try to load the symbolsBasic eralies 14 days before release date
|
||||||
|
private static final Logger logger = Logger.getLogger(GathererSets.class);
|
||||||
|
|
||||||
private static final String SETS_PATH = File.separator + "sets";
|
private static final String SETS_PATH = File.separator + "sets";
|
||||||
private static final File DEFAULT_OUT_DIR = new File("plugins" + File.separator + "images" + SETS_PATH);
|
private static final File DEFAULT_OUT_DIR = new File("plugins" + File.separator + "images" + SETS_PATH);
|
||||||
private static File outDir = DEFAULT_OUT_DIR;
|
private static File outDir = DEFAULT_OUT_DIR;
|
||||||
|
|
||||||
private static final String[] symbols = {"10E", "9ED", "8ED", "7ED", "6ED", "5ED", "4ED", "3ED", "2ED", "LEB", "LEA",
|
private static final String[] symbolsBasic = {"10E", "9ED", "8ED", "7ED", "6ED", "5ED", "4ED", "3ED", "2ED", "LEB", "LEA",
|
||||||
"HOP",
|
"HOP",
|
||||||
"ARN", "ATQ", "LEG", "DRK", "FEM", "HML",
|
"ARN", "ATQ", "LEG", "DRK", "FEM", "HML",
|
||||||
"ICE", "ALL", "CSP",
|
"ICE", "ALL", "CSP",
|
||||||
|
@ -36,14 +61,19 @@ public class GathererSets implements Iterable<DownloadJob> {
|
||||||
"LRW", "MOR",
|
"LRW", "MOR",
|
||||||
"SHM", "EVE",
|
"SHM", "EVE",
|
||||||
"MED", "ME2", "ME3", "ME4",
|
"MED", "ME2", "ME3", "ME4",
|
||||||
"POR", "PO2", "PTK",
|
"POR", "P02", "PTK",
|
||||||
"ARC", "DD3EVG",
|
"ARC", "DD3EVG",
|
||||||
"W16", "W17"};
|
"W16", "W17",
|
||||||
|
//"APAC" -- gatherer do not have that set, scrly have PALP
|
||||||
|
//"ARENA" -- is't many set with different codes, not one
|
||||||
|
"CLASH", "CP", "DD3GVL", "DPA", "EURO", "FNMP", "GPX", "GRC", "GUR", "H17", "JR", "MBP", "MGDC", "MLP", "MPRP", "MPS-AKH", "PTC", "S00", "S99", "SUS", "SWS", "UGIN", "UGL", "V10", "V17", "WMCQ", // need to fix
|
||||||
|
"H09", "PD2", "PD3", "UNH", "CM1", "E02", "V11", "M25", "UST", "IMA", "DD2", "EVG", "DDC", "DDE", "DDD", "DDT", "8EB", "9EB", "CHR" // ok
|
||||||
|
// current testing
|
||||||
|
};
|
||||||
|
|
||||||
private static final String[] withMythics = {"M10", "M11", "M12", "M13", "M14", "M15", "ORI",
|
private static final String[] symbolsBasicWithMyth = {"M10", "M11", "M12", "M13", "M14", "M15", "ORI",
|
||||||
"ANB",
|
|
||||||
"DDF", "DDG", "DDH", "DDI", "DDJ", "DDK", "DDL", "DDM", "DDN",
|
"DDF", "DDG", "DDH", "DDI", "DDJ", "DDK", "DDL", "DDM", "DDN",
|
||||||
"DD3DVD", "DD3GLV", "DD3JVC", "DDO", "DDP", "DDQ", "DDR", "DDS",
|
"DD3DVD", "DD3JVC", "DDO", "DDP", "DDQ", "DDR", "DDS",
|
||||||
"ALA", "CON", "ARB",
|
"ALA", "CON", "ARB",
|
||||||
"ZEN", "WWK", "ROE",
|
"ZEN", "WWK", "ROE",
|
||||||
"SOM", "MBS", "NPH",
|
"SOM", "MBS", "NPH",
|
||||||
|
@ -60,60 +90,64 @@ public class GathererSets implements Iterable<DownloadJob> {
|
||||||
"SOI", "EMN",
|
"SOI", "EMN",
|
||||||
"KLD", "AER",
|
"KLD", "AER",
|
||||||
"AKH", "HOU",
|
"AKH", "HOU",
|
||||||
|
"XLN", "C17",
|
||||||
|
"RIX", "DOM", "M19", // not released
|
||||||
"E01"
|
"E01"
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final String[] onlyMythics = {
|
private static final String[] symbolsOnlyMyth = {
|
||||||
"DRB", "V09", "V12", "V12", "V13", "V14", "V15", "V16", "EXP"
|
"DRB", "V09", "V12", "V13", "V14", "V15", "V16", "EXP"
|
||||||
};
|
};
|
||||||
private static final String[] onlyMythicsAsSpecial = {
|
private static final String[] symbolsOnlySpecial = {
|
||||||
"MPS"
|
"MPS"
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final HashMap<String, String> symbolsReplacements = new HashMap<>();
|
private static final HashMap<String, String> codeReplacements = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
symbolsReplacements.put("2ED", "2U");
|
codeReplacements.put("2ED", "2U");
|
||||||
symbolsReplacements.put("3ED", "3E");
|
codeReplacements.put("3ED", "3E");
|
||||||
symbolsReplacements.put("4ED", "4E");
|
codeReplacements.put("4ED", "4E");
|
||||||
symbolsReplacements.put("5ED", "5E");
|
codeReplacements.put("5ED", "5E");
|
||||||
symbolsReplacements.put("6ED", "6E");
|
codeReplacements.put("6ED", "6E");
|
||||||
symbolsReplacements.put("7ED", "7E");
|
codeReplacements.put("7ED", "7E");
|
||||||
symbolsReplacements.put("ALL", "AL");
|
codeReplacements.put("ALL", "AL");
|
||||||
symbolsReplacements.put("APC", "AP");
|
codeReplacements.put("APC", "AP");
|
||||||
symbolsReplacements.put("ARN", "AN");
|
codeReplacements.put("ARN", "AN");
|
||||||
symbolsReplacements.put("ATQ", "AQ");
|
codeReplacements.put("ATQ", "AQ");
|
||||||
symbolsReplacements.put("CMA", "CM1");
|
codeReplacements.put("CMA", "CM1");
|
||||||
symbolsReplacements.put("DD3DVD", "DD3_DVD");
|
codeReplacements.put("DD3DVD", "DD3_DVD");
|
||||||
symbolsReplacements.put("DD3EVG", "DD3_EVG");
|
codeReplacements.put("DD3EVG", "DD3_EVG");
|
||||||
symbolsReplacements.put("DD3GLV", "DD3_GLV");
|
codeReplacements.put("DD3JVC", "DD3_JVC");
|
||||||
symbolsReplacements.put("DD3JVC", "DD3_JVC");
|
codeReplacements.put("DRK", "DK");
|
||||||
symbolsReplacements.put("DRK", "DK");
|
codeReplacements.put("EXO", "EX");
|
||||||
symbolsReplacements.put("EXO", "EX");
|
codeReplacements.put("FEM", "FE");
|
||||||
symbolsReplacements.put("FEM", "FE");
|
codeReplacements.put("HML", "HM");
|
||||||
symbolsReplacements.put("HML", "HM");
|
codeReplacements.put("ICE", "IA");
|
||||||
symbolsReplacements.put("ICE", "IA");
|
codeReplacements.put("INV", "IN");
|
||||||
symbolsReplacements.put("INV", "IN");
|
codeReplacements.put("LEA", "1E");
|
||||||
symbolsReplacements.put("LEA", "1E");
|
codeReplacements.put("LEB", "2E");
|
||||||
symbolsReplacements.put("LEB", "2E");
|
codeReplacements.put("LEG", "LE");
|
||||||
symbolsReplacements.put("LEG", "LE");
|
codeReplacements.put("MPS", "MPS_KLD");
|
||||||
symbolsReplacements.put("MPS", "MPS_KLD");
|
codeReplacements.put("MIR", "MI");
|
||||||
symbolsReplacements.put("MIR", "MI");
|
codeReplacements.put("MMQ", "MM");
|
||||||
symbolsReplacements.put("MMQ", "MM");
|
codeReplacements.put("NEM", "NE");
|
||||||
symbolsReplacements.put("NEM", "NE");
|
codeReplacements.put("ODY", "OD");
|
||||||
symbolsReplacements.put("ODY", "OD");
|
codeReplacements.put("PCY", "PR");
|
||||||
symbolsReplacements.put("PCY", "PR");
|
codeReplacements.put("PLS", "PS");
|
||||||
symbolsReplacements.put("PLS", "PS");
|
codeReplacements.put("POR", "PO");
|
||||||
symbolsReplacements.put("POR", "PO");
|
codeReplacements.put("P02", "P2");
|
||||||
symbolsReplacements.put("PO2", "P2");
|
codeReplacements.put("PTK", "PK");
|
||||||
symbolsReplacements.put("PTK", "PK");
|
codeReplacements.put("STH", "ST");
|
||||||
symbolsReplacements.put("STH", "ST");
|
codeReplacements.put("TMP", "TE");
|
||||||
symbolsReplacements.put("TMP", "TE");
|
codeReplacements.put("UDS", "CG");
|
||||||
symbolsReplacements.put("UDS", "CG");
|
codeReplacements.put("ULG", "GU");
|
||||||
symbolsReplacements.put("ULG", "GU");
|
codeReplacements.put("USG", "UZ");
|
||||||
symbolsReplacements.put("USG", "UZ");
|
codeReplacements.put("VIS", "VI");
|
||||||
symbolsReplacements.put("VIS", "VI");
|
codeReplacements.put("WTH", "WL");
|
||||||
symbolsReplacements.put("WTH", "WL");
|
codeReplacements.put("8EB", "8ED"); // inner xmage set for 8th edition
|
||||||
|
codeReplacements.put("9EB", "8ED"); // inner xmage set for 9th edition
|
||||||
|
codeReplacements.put("CHR", "CH");
|
||||||
}
|
}
|
||||||
|
|
||||||
public GathererSets(String path) {
|
public GathererSets(String path) {
|
||||||
|
@ -124,49 +158,155 @@ public class GathererSets implements Iterable<DownloadJob> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checks for wrong card settings and support (easy to control what all good)
|
||||||
|
private static final HashMap<String, CheckResult> setsToDownload = new HashMap<>();
|
||||||
|
private static final HashMap<String, String> codesToIgnoreCheck = new HashMap<>();
|
||||||
|
static {
|
||||||
|
// xMage have inner sets for 8th and 9th Edition for booster workaround (cards from core game do not include in boosters)
|
||||||
|
// see https://mtg.gamepedia.com/8th_Edition/Core_Game
|
||||||
|
// check must ignore that sets
|
||||||
|
codesToIgnoreCheck.put("8EB", "8th Edition Box");
|
||||||
|
codesToIgnoreCheck.put("9EB", "9th Edition Box");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckSearchResult(String searchCode, ExpansionSet foundedExp, boolean canDownloadTask,
|
||||||
|
boolean haveCommon, boolean haveUncommon, boolean haveRare, boolean haveMyth){
|
||||||
|
// duplicated in settings
|
||||||
|
CheckResult res = setsToDownload.get(searchCode);
|
||||||
|
|
||||||
|
if (res != null) {
|
||||||
|
logger.error(String.format("Symbols: founded duplicated code: %s", searchCode));
|
||||||
|
} else {
|
||||||
|
res = new CheckResult(searchCode, foundedExp, haveCommon, haveUncommon, haveRare, haveMyth);
|
||||||
|
setsToDownload.put(searchCode, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// not found
|
||||||
|
if (foundedExp == null) {
|
||||||
|
logger.error(String.format("Symbols: can't find set by code: %s", searchCode));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks for founded sets only
|
||||||
|
|
||||||
|
// to early to download
|
||||||
|
if (!canDownloadTask){
|
||||||
|
Calendar c = Calendar.getInstance();
|
||||||
|
c.setTime(foundedExp.getReleaseDate());
|
||||||
|
c.add(Calendar.DATE, -1 * DAYS_BEFORE_RELEASE_TO_DOWNLOAD);
|
||||||
|
logger.warn(String.format("Symbols: early to download: %s (%s), available after %s",
|
||||||
|
searchCode, foundedExp.getName(), c.getTime()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AnalyseSearchResult(){
|
||||||
|
// analyze supported sets and show wrong settings
|
||||||
|
Date startedDate = new Date();
|
||||||
|
|
||||||
|
for (ExpansionSet set : Sets.getInstance().values()) {
|
||||||
|
|
||||||
|
// ignore some inner sets
|
||||||
|
if (codesToIgnoreCheck.get(set.getCode()) != null){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckResult res = setsToDownload.get(set.getCode());
|
||||||
|
|
||||||
|
// 1. not configured at all
|
||||||
|
if (res == null) {
|
||||||
|
logger.warn(String.format("Symbols: set is not configured: %s (%s)", set.getCode(), set.getName()));
|
||||||
|
continue; // can't do other checks
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. missing rarity icon:
|
||||||
|
// WARNING, need too much time (60+ secs), only for debug mode
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
if ((set.getCardsByRarity(Rarity.COMMON).size() > 0) && !res.haveCommon) {
|
||||||
|
logger.error(String.format("Symbols: set have common cards, but don't download icon: %s (%s)", set.getCode(), set.getName()));
|
||||||
|
}
|
||||||
|
if ((set.getCardsByRarity(Rarity.UNCOMMON).size() > 0) && !res.haveUncommon) {
|
||||||
|
logger.error(String.format("Symbols: set have uncommon cards, but don't download icon: %s (%s)", set.getCode(), set.getName()));
|
||||||
|
}
|
||||||
|
if ((set.getCardsByRarity(Rarity.RARE).size() > 0) && !res.haveRare) {
|
||||||
|
logger.error(String.format("Symbols: set have rare cards, but don't download icon: %s (%s)", set.getCode(), set.getName()));
|
||||||
|
}
|
||||||
|
if ((set.getCardsByRarity(Rarity.MYTHIC).size() > 0) && !res.haveMyth) {
|
||||||
|
logger.error(String.format("Symbols: set have mythic cards, but don't download icon: %s (%s)", set.getCode(), set.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Date endedDate = new Date();
|
||||||
|
long secs = (endedDate.getTime() - startedDate.getTime()) / 1000;
|
||||||
|
logger.debug(String.format("Symbols: check completed after %d seconds", secs));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<DownloadJob> iterator() {
|
public Iterator<DownloadJob> iterator() {
|
||||||
Calendar c = Calendar.getInstance();
|
Calendar c = Calendar.getInstance();
|
||||||
c.setTime(new Date());
|
c.setTime(new Date());
|
||||||
c.add(Calendar.DATE, +14); // Try to load the symbols eralies 14 days before release date
|
c.add(Calendar.DATE, DAYS_BEFORE_RELEASE_TO_DOWNLOAD);
|
||||||
Date compareDate = c.getTime();
|
Date compareDate = c.getTime();
|
||||||
ArrayList<DownloadJob> jobs = new ArrayList<>();
|
ArrayList<DownloadJob> jobs = new ArrayList<>();
|
||||||
for (String symbol : symbols) {
|
boolean canDownload;
|
||||||
|
|
||||||
|
setsToDownload.clear();
|
||||||
|
|
||||||
|
for (String symbol : symbolsBasic) {
|
||||||
ExpansionSet exp = Sets.findSet(symbol);
|
ExpansionSet exp = Sets.findSet(symbol);
|
||||||
|
canDownload = false;
|
||||||
if (exp != null && exp.getReleaseDate().before(compareDate)) {
|
if (exp != null && exp.getReleaseDate().before(compareDate)) {
|
||||||
|
canDownload = true;
|
||||||
jobs.add(generateDownloadJob(symbol, "C", "C"));
|
jobs.add(generateDownloadJob(symbol, "C", "C"));
|
||||||
jobs.add(generateDownloadJob(symbol, "U", "U"));
|
jobs.add(generateDownloadJob(symbol, "U", "U"));
|
||||||
jobs.add(generateDownloadJob(symbol, "R", "R"));
|
jobs.add(generateDownloadJob(symbol, "R", "R"));
|
||||||
}
|
}
|
||||||
|
CheckSearchResult(symbol, exp, canDownload, true, true, true, false);
|
||||||
}
|
}
|
||||||
for (String symbol : withMythics) {
|
|
||||||
|
for (String symbol : symbolsBasicWithMyth) {
|
||||||
ExpansionSet exp = Sets.findSet(symbol);
|
ExpansionSet exp = Sets.findSet(symbol);
|
||||||
|
canDownload = false;
|
||||||
if (exp != null && exp.getReleaseDate().before(compareDate)) {
|
if (exp != null && exp.getReleaseDate().before(compareDate)) {
|
||||||
|
canDownload = true;
|
||||||
jobs.add(generateDownloadJob(symbol, "C", "C"));
|
jobs.add(generateDownloadJob(symbol, "C", "C"));
|
||||||
jobs.add(generateDownloadJob(symbol, "U", "U"));
|
jobs.add(generateDownloadJob(symbol, "U", "U"));
|
||||||
jobs.add(generateDownloadJob(symbol, "R", "R"));
|
jobs.add(generateDownloadJob(symbol, "R", "R"));
|
||||||
jobs.add(generateDownloadJob(symbol, "M", "M"));
|
jobs.add(generateDownloadJob(symbol, "M", "M"));
|
||||||
}
|
}
|
||||||
|
CheckSearchResult(symbol, exp, canDownload, true, true, true, true);
|
||||||
}
|
}
|
||||||
for (String symbol : onlyMythics) {
|
|
||||||
|
for (String symbol : symbolsOnlyMyth) {
|
||||||
ExpansionSet exp = Sets.findSet(symbol);
|
ExpansionSet exp = Sets.findSet(symbol);
|
||||||
|
canDownload = false;
|
||||||
if (exp != null && exp.getReleaseDate().before(compareDate)) {
|
if (exp != null && exp.getReleaseDate().before(compareDate)) {
|
||||||
|
canDownload = true;
|
||||||
jobs.add(generateDownloadJob(symbol, "M", "M"));
|
jobs.add(generateDownloadJob(symbol, "M", "M"));
|
||||||
}
|
}
|
||||||
|
CheckSearchResult(symbol, exp, canDownload, false, false, false, true);
|
||||||
}
|
}
|
||||||
for (String symbol : onlyMythicsAsSpecial) {
|
|
||||||
|
for (String symbol : symbolsOnlySpecial) {
|
||||||
ExpansionSet exp = Sets.findSet(symbol);
|
ExpansionSet exp = Sets.findSet(symbol);
|
||||||
|
canDownload = false;
|
||||||
if (exp != null && exp.getReleaseDate().before(compareDate)) {
|
if (exp != null && exp.getReleaseDate().before(compareDate)) {
|
||||||
|
canDownload = true;
|
||||||
jobs.add(generateDownloadJob(symbol, "M", "S"));
|
jobs.add(generateDownloadJob(symbol, "M", "S"));
|
||||||
}
|
}
|
||||||
|
CheckSearchResult(symbol, exp, canDownload, false, false, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check wrong settings
|
||||||
|
AnalyseSearchResult();
|
||||||
|
|
||||||
return jobs.iterator();
|
return jobs.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
private DownloadJob generateDownloadJob(String set, String rarity, String urlRarity) {
|
private DownloadJob generateDownloadJob(String set, String rarity, String urlRarity) {
|
||||||
File dst = new File(outDir, set + '-' + rarity + ".jpg");
|
File dst = new File(outDir, set + '-' + rarity + ".jpg");
|
||||||
if (symbolsReplacements.containsKey(set)) {
|
if (codeReplacements.containsKey(set)) {
|
||||||
set = symbolsReplacements.get(set);
|
set = codeReplacements.get(set);
|
||||||
}
|
}
|
||||||
String url = "http://gatherer.wizards.com/Handlers/Image.ashx?type=symbol&set=" + set + "&size=small&rarity=" + urlRarity;
|
String url = "http://gatherer.wizards.com/Handlers/Image.ashx?type=symbol&set=" + set + "&size=small&rarity=" + urlRarity;
|
||||||
return new DownloadJob(set + '-' + rarity, fromURL(url), toFile(dst));
|
return new DownloadJob(set + '-' + rarity, fromURL(url), toFile(dst));
|
||||||
|
|
|
@ -214,10 +214,18 @@ public enum ScryfallImageSource implements CardImageSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String generateURL(CardDownloadData card) throws Exception {
|
public String generateURL(CardDownloadData card) throws Exception {
|
||||||
|
|
||||||
|
if (card.isTwoFacedCard()) {
|
||||||
|
// double faced cards do not supporte by API (need direct link for img)
|
||||||
|
// example: https://img.scryfall.com/cards/large/en/xln/173b.jpg
|
||||||
|
return "https://img.scryfall.com/cards/large/en/" + formatSetName(card.getSet()) + "/"
|
||||||
|
+ card.getCollectorId() + (card.isSecondSide() ? "b" : "a") + ".jpg";
|
||||||
|
}else {
|
||||||
|
// single face cards support by single API call (redirect to img link)
|
||||||
|
// example: https://api.scryfall.com/cards/xln/121?format=image
|
||||||
return "https://api.scryfall.com/cards/" + formatSetName(card.getSet()) + "/"
|
return "https://api.scryfall.com/cards/" + formatSetName(card.getSet()) + "/"
|
||||||
+ card.getCollectorId()
|
+ card.getCollectorId() + "?format=image";
|
||||||
+ (card.isSecondSide() ? "b" : "")
|
}
|
||||||
+ "?format=image";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -451,6 +451,7 @@ public enum WizardCardsImageSource implements CardImageSource {
|
||||||
setsAliases.put("WMCQ", "World Magic Cup Qualifier");
|
setsAliases.put("WMCQ", "World Magic Cup Qualifier");
|
||||||
setsAliases.put("WTH", "Weatherlight");
|
setsAliases.put("WTH", "Weatherlight");
|
||||||
setsAliases.put("WWK", "Worldwake");
|
setsAliases.put("WWK", "Worldwake");
|
||||||
|
setsAliases.put("XLN", "Ixalan");
|
||||||
setsAliases.put("ZEN", "Zendikar");
|
setsAliases.put("ZEN", "Zendikar");
|
||||||
|
|
||||||
languageAliases = new HashMap<>();
|
languageAliases = new HashMap<>();
|
||||||
|
|
|
@ -3,7 +3,9 @@ package org.mage.plugins.card.images;
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.collect.ComputationException;
|
import com.google.common.collect.ComputationException;
|
||||||
import com.google.common.collect.MapMaker;
|
import com.google.common.collect.MapMaker;
|
||||||
import java.awt.Graphics2D;
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -44,6 +46,7 @@ public final class ImageCache {
|
||||||
private static final Logger LOGGER = Logger.getLogger(ImageCache.class);
|
private static final Logger LOGGER = Logger.getLogger(ImageCache.class);
|
||||||
|
|
||||||
private static final Map<String, BufferedImage> IMAGE_CACHE;
|
private static final Map<String, BufferedImage> IMAGE_CACHE;
|
||||||
|
private static final Map<String, BufferedImage> FACE_IMAGE_CACHE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common pattern for keys. Format: "<cardname>#<setname>#<collectorID>"
|
* Common pattern for keys. Format: "<cardname>#<setname>#<collectorID>"
|
||||||
|
@ -81,12 +84,14 @@ public final class ImageCache {
|
||||||
|
|
||||||
CardDownloadData info = new CardDownloadData(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor);
|
CardDownloadData info = new CardDownloadData(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor);
|
||||||
|
|
||||||
|
boolean cardback = false;
|
||||||
String path;
|
String path;
|
||||||
if (collectorId.isEmpty() || "0".equals(collectorId)) {
|
if (collectorId.isEmpty() || "0".equals(collectorId)) {
|
||||||
info.setToken(true);
|
info.setToken(true);
|
||||||
path = CardImageUtils.generateTokenImagePath(info);
|
path = CardImageUtils.generateTokenImagePath(info);
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename;
|
cardback = true;
|
||||||
|
path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename; // TODO: replace empty token by other default card, not cardback
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
path = CardImageUtils.generateImagePath(info);
|
path = CardImageUtils.generateImagePath(info);
|
||||||
|
@ -101,6 +106,7 @@ public final class ImageCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thumbnail && path.endsWith(".jpg")) {
|
if (thumbnail && path.endsWith(".jpg")) {
|
||||||
|
// need thumbnail image
|
||||||
String thumbnailPath = buildThumbnailPath(path);
|
String thumbnailPath = buildThumbnailPath(path);
|
||||||
TFile thumbnailFile = null;
|
TFile thumbnailFile = null;
|
||||||
try {
|
try {
|
||||||
|
@ -118,20 +124,36 @@ public final class ImageCache {
|
||||||
if (exists) {
|
if (exists) {
|
||||||
LOGGER.debug("loading thumbnail for " + key + ", path=" + thumbnailPath);
|
LOGGER.debug("loading thumbnail for " + key + ", path=" + thumbnailPath);
|
||||||
BufferedImage thumbnailImage = loadImage(thumbnailFile);
|
BufferedImage thumbnailImage = loadImage(thumbnailFile);
|
||||||
|
|
||||||
if (thumbnailImage == null) { // thumbnail exists but broken for some reason
|
if (thumbnailImage == null) { // thumbnail exists but broken for some reason
|
||||||
LOGGER.warn("failed loading thumbnail for " + key + ", path=" + thumbnailPath
|
LOGGER.warn("failed loading thumbnail for " + key + ", path=" + thumbnailPath
|
||||||
+ ", thumbnail file is probably broken, attempting to recreate it...");
|
+ ", thumbnail file is probably broken, attempting to recreate it...");
|
||||||
thumbnailImage = makeThumbnailByFile(key, file, thumbnailPath);
|
thumbnailImage = makeThumbnailByFile(key, file, thumbnailPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cardback){
|
||||||
|
// unknown tokens on opponent desk
|
||||||
|
thumbnailImage = getRoundCorner(thumbnailImage);
|
||||||
|
}
|
||||||
|
|
||||||
return thumbnailImage;
|
return thumbnailImage;
|
||||||
} else {
|
} else {
|
||||||
return makeThumbnailByFile(key, file, thumbnailPath);
|
return makeThumbnailByFile(key, file, thumbnailPath);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (cardback){
|
||||||
|
// need cardback image
|
||||||
|
BufferedImage image = loadImage(file);
|
||||||
|
image = getRoundCorner(image);
|
||||||
|
return image;
|
||||||
|
}else {
|
||||||
|
// need normal card image
|
||||||
BufferedImage image = loadImage(file);
|
BufferedImage image = loadImage(file);
|
||||||
image = getWizardsCard(image);
|
image = getWizardsCard(image);
|
||||||
|
image = getRoundCorner(image);
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"Requested image doesn't fit the requirement for key (<cardname>#<setname>#<collectorID>): " + key);
|
"Requested image doesn't fit the requirement for key (<cardname>#<setname>#<collectorID>): " + key);
|
||||||
|
@ -145,6 +167,56 @@ public final class ImageCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BufferedImage makeThumbnailByFile(String key, TFile file, String thumbnailPath) {
|
||||||
|
BufferedImage image = loadImage(file);
|
||||||
|
image = getWizardsCard(image);
|
||||||
|
image = getRoundCorner(image);
|
||||||
|
if (image == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LOGGER.debug("creating thumbnail for " + key);
|
||||||
|
return makeThumbnail(image, thumbnailPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FACE_IMAGE_CACHE = new MapMaker().softValues().makeComputingMap(new Function<String, BufferedImage>() {
|
||||||
|
@Override
|
||||||
|
public BufferedImage apply(String key) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Matcher m = KEY_PATTERN.matcher(key);
|
||||||
|
|
||||||
|
if (m.matches()) {
|
||||||
|
String name = m.group(1);
|
||||||
|
String set = m.group(2);
|
||||||
|
//Integer artid = Integer.parseInt(m.group(2));
|
||||||
|
|
||||||
|
String path;
|
||||||
|
path = CardImageUtils.generateFaceImagePath(name, set);
|
||||||
|
|
||||||
|
if (path == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
TFile file = getTFile(path);
|
||||||
|
if (file == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedImage image = loadImage(file);
|
||||||
|
return image;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Requested face image doesn't fit the requirement for key (<cardname>#<artid>#: " + key);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
if (ex instanceof ComputationException) {
|
||||||
|
throw (ComputationException) ex;
|
||||||
|
} else {
|
||||||
|
throw new ComputationException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public BufferedImage makeThumbnailByFile(String key, TFile file, String thumbnailPath) {
|
public BufferedImage makeThumbnailByFile(String key, TFile file, String thumbnailPath) {
|
||||||
BufferedImage image = loadImage(file);
|
BufferedImage image = loadImage(file);
|
||||||
image = getWizardsCard(image);
|
image = getWizardsCard(image);
|
||||||
|
@ -189,7 +261,7 @@ public final class ImageCache {
|
||||||
info.setToken(true);
|
info.setToken(true);
|
||||||
path = CardImageUtils.generateFullTokenImagePath(info);
|
path = CardImageUtils.generateFullTokenImagePath(info);
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename;
|
path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename; // TODO: replace empty token by other default card, not cardback
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
path = CardImageUtils.generateImagePath(info);
|
path = CardImageUtils.generateImagePath(info);
|
||||||
|
@ -207,6 +279,12 @@ public final class ImageCache {
|
||||||
private ImageCache() {
|
private ImageCache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BufferedImage getCardbackImage() {
|
||||||
|
BufferedImage image = ImageCache.loadImage(new TFile(DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename));
|
||||||
|
image = getRoundCorner(image);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
public static BufferedImage getMorphImage() {
|
public static BufferedImage getMorphImage() {
|
||||||
CardDownloadData info = new CardDownloadData("Morph", "KTK", "0", false, 0, "KTK", "");
|
CardDownloadData info = new CardDownloadData("Morph", "KTK", "0", false, 0, "KTK", "");
|
||||||
info.setToken(true);
|
info.setToken(true);
|
||||||
|
@ -215,7 +293,10 @@ public final class ImageCache {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
TFile file = getTFile(path);
|
TFile file = getTFile(path);
|
||||||
return loadImage(file);
|
|
||||||
|
BufferedImage image = loadImage(file);
|
||||||
|
image = getRoundCorner(image);
|
||||||
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BufferedImage getManifestImage() {
|
public static BufferedImage getManifestImage() {
|
||||||
|
@ -226,7 +307,10 @@ public final class ImageCache {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
TFile file = getTFile(path);
|
TFile file = getTFile(path);
|
||||||
return loadImage(file);
|
|
||||||
|
BufferedImage image = loadImage(file);
|
||||||
|
image = getRoundCorner(image);
|
||||||
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String buildThumbnailPath(String path) {
|
private static String buildThumbnailPath(String path) {
|
||||||
|
@ -239,6 +323,32 @@ public final class ImageCache {
|
||||||
return thumbnailPath;
|
return thumbnailPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BufferedImage getRoundCorner(BufferedImage image){
|
||||||
|
if (image != null) {
|
||||||
|
BufferedImage cornerImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||||
|
|
||||||
|
// corner
|
||||||
|
float ROUNDED_CORNER_SIZE = 0.15f; // see CardPanelComponentImpl
|
||||||
|
int cornerSizeBorder = Math.max(4, Math.round(image.getWidth() * ROUNDED_CORNER_SIZE));
|
||||||
|
|
||||||
|
// corner mask
|
||||||
|
Graphics2D gg = cornerImage.createGraphics();
|
||||||
|
gg.setComposite(AlphaComposite.Src);
|
||||||
|
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
gg.setColor(Color.white);
|
||||||
|
gg.fill(new RoundRectangle2D.Float(0, 0, cornerImage.getWidth(), cornerImage.getHeight(), cornerSizeBorder, cornerSizeBorder));
|
||||||
|
|
||||||
|
// image draw to buffer
|
||||||
|
gg.setComposite(AlphaComposite.SrcAtop);
|
||||||
|
gg.drawImage(image, 0, 0, null);
|
||||||
|
//gg.dispose();
|
||||||
|
|
||||||
|
return cornerImage;
|
||||||
|
} else {
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static BufferedImage getWizardsCard(BufferedImage image) {
|
public static BufferedImage getWizardsCard(BufferedImage image) {
|
||||||
if (image != null && image.getWidth() == 265 && image.getHeight() == 370) {
|
if (image != null && image.getWidth() == 265 && image.getHeight() == 370) {
|
||||||
BufferedImage crop = new BufferedImage(256, 360, BufferedImage.TYPE_INT_RGB);
|
BufferedImage crop = new BufferedImage(256, 360, BufferedImage.TYPE_INT_RGB);
|
||||||
|
@ -263,6 +373,10 @@ public final class ImageCache {
|
||||||
return getImage(getKey(card, card.getName(), ""));
|
return getImage(getKey(card, card.getName(), ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BufferedImage getImageFaceOriginal(CardView card) {
|
||||||
|
return getFaceImage(getFaceKey(card, card.getName(), card.getExpansionSetCode()));
|
||||||
|
}
|
||||||
|
|
||||||
public static BufferedImage getImageOriginalAlternateName(CardView card) {
|
public static BufferedImage getImageOriginalAlternateName(CardView card) {
|
||||||
return getImage(getKey(card, card.getAlternateName(), ""));
|
return getImage(getKey(card, card.getAlternateName(), ""));
|
||||||
}
|
}
|
||||||
|
@ -288,6 +402,27 @@ public final class ImageCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Image corresponding to the key
|
||||||
|
*/
|
||||||
|
private static BufferedImage getFaceImage(String key) {
|
||||||
|
try {
|
||||||
|
return FACE_IMAGE_CACHE.get(key);
|
||||||
|
} catch (NullPointerException ex) {
|
||||||
|
// unfortunately NullOutputException, thrown when apply() returns
|
||||||
|
// null, is not public
|
||||||
|
// NullOutputException is a subclass of NullPointerException
|
||||||
|
// legitimate, happens when a card has no image
|
||||||
|
return null;
|
||||||
|
} catch (ComputationException ex) {
|
||||||
|
if (ex.getCause() instanceof NullPointerException) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LOGGER.error(ex, ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Image corresponding to the key only if it already exists in
|
* Returns the Image corresponding to the key only if it already exists in
|
||||||
* the cache.
|
* the cache.
|
||||||
|
@ -296,6 +431,14 @@ public final class ImageCache {
|
||||||
return IMAGE_CACHE.containsKey(key) ? IMAGE_CACHE.get(key) : null;
|
return IMAGE_CACHE.containsKey(key) ? IMAGE_CACHE.get(key) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Image corresponding to the key only if it already exists in
|
||||||
|
* the cache.
|
||||||
|
*/
|
||||||
|
private static BufferedImage tryGetFaceImage(String key) {
|
||||||
|
return FACE_IMAGE_CACHE.containsKey(key) ? FACE_IMAGE_CACHE.get(key) : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the map key for a card, without any suffixes for the image size.
|
* Returns the map key for a card, without any suffixes for the image size.
|
||||||
*/
|
*/
|
||||||
|
@ -307,6 +450,13 @@ public final class ImageCache {
|
||||||
+ (card.getTokenDescriptor() != null ? '#' + card.getTokenDescriptor() : "#");
|
+ (card.getTokenDescriptor() != null ? '#' + card.getTokenDescriptor() : "#");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the map key for a card, without any suffixes for the image size.
|
||||||
|
*/
|
||||||
|
private static String getFaceKey(CardView card, String name, String set) {
|
||||||
|
return name + '#' + set + "####";
|
||||||
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * Returns the map key for the flip image of a card, without any suffixes for the image size.
|
// * Returns the map key for the flip image of a card, without any suffixes for the image size.
|
||||||
// */
|
// */
|
||||||
|
@ -314,6 +464,7 @@ public final class ImageCache {
|
||||||
// return alternateName + "#" + card.getExpansionSetCode() + "#" +card.getType()+ "#" + card.getCardNumber() + "#"
|
// return alternateName + "#" + card.getExpansionSetCode() + "#" +card.getType()+ "#" + card.getCardNumber() + "#"
|
||||||
// + (card.getTokenSetCode() == null ? "":card.getTokenSetCode());
|
// + (card.getTokenSetCode() == null ? "":card.getTokenSetCode());
|
||||||
// }
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load image from file
|
* Load image from file
|
||||||
*
|
*
|
||||||
|
@ -409,6 +560,25 @@ public final class ImageCache {
|
||||||
return TransformedImageCache.getResizedImage(original, (int) (original.getWidth() * scale), (int) (original.getHeight() * scale));
|
return TransformedImageCache.getResizedImage(original, (int) (original.getWidth() * scale), (int) (original.getHeight() * scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the image appropriate to display the card in the picture panel
|
||||||
|
*
|
||||||
|
* @param card
|
||||||
|
* @param width
|
||||||
|
* @param height
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static BufferedImage getFaceImage(CardView card, int width, int height) {
|
||||||
|
String key = getFaceKey(card, card.getName(), card.getExpansionSetCode());
|
||||||
|
BufferedImage original = getFaceImage(key);
|
||||||
|
if (original == null) {
|
||||||
|
LOGGER.debug(key + " (faceimage) not found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the image appropriate to display for a card in a picture panel,
|
* 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
|
* but only it was ALREADY LOADED. That is, the call is immediate and will
|
||||||
|
|
|
@ -204,6 +204,14 @@ public final class CardImageUtils {
|
||||||
return imageDir + TFile.separator + imageName;
|
return imageDir + TFile.separator + imageName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String generateFaceImagePath(String cardname, String set) {
|
||||||
|
String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
|
||||||
|
String imagesPath = Objects.equals(useDefault, "true") ? Constants.IO.imageBaseDir : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
|
||||||
|
String imageDir = imagesPath;
|
||||||
|
String imageName = set + TFile.separator + cardname + ".jpg";
|
||||||
|
return imageDir + TFile.separator + "FACE" + TFile.separator + imageName;
|
||||||
|
}
|
||||||
|
|
||||||
public static String generateTokenDescriptorImagePath(CardDownloadData card) {
|
public static String generateTokenDescriptorImagePath(CardDownloadData card) {
|
||||||
// String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
|
// String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
|
||||||
// String imagesPath = Objects.equals(useDefault, "true") ? null : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
|
// String imagesPath = Objects.equals(useDefault, "true") ? null : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
|
||||||
|
|
|
@ -25,8 +25,8 @@ public abstract class MageCard extends JPanel {
|
||||||
|
|
||||||
public abstract CardView getOriginal();
|
public abstract CardView getOriginal();
|
||||||
|
|
||||||
// sets the vertical text offset for the card name on the image
|
// sets the vertical text offset for the card name on the image, use to move caption to card center
|
||||||
public abstract void setTextOffset(int yOffset);
|
public abstract void setCardCaptionTopOffset(int yOffsetPercent);
|
||||||
|
|
||||||
public abstract void setCardBounds(int x, int y, int width, int height);
|
public abstract void setCardBounds(int x, int y, int width, int height);
|
||||||
|
|
||||||
|
|
|
@ -117,9 +117,9 @@ public class CanadianHighlander extends Constructed {
|
||||||
|| cn.equals("Mana Crypt")
|
|| cn.equals("Mana Crypt")
|
||||||
|| cn.equals("Mystical Tutor")
|
|| cn.equals("Mystical Tutor")
|
||||||
|| cn.equals("Strip Mine")
|
|| cn.equals("Strip Mine")
|
||||||
|| cn.equals("Summoner’s Pact")
|
|| cn.equals("Summoner's Pact")
|
||||||
|| cn.equals("Survival of the Fittest")
|
|| cn.equals("Survival of the Fittest")
|
||||||
|| cn.equals("Umezawa’s Jitte")) {
|
|| cn.equals("Umezawa's Jitte")) {
|
||||||
totalPoints += 2;
|
totalPoints += 2;
|
||||||
invalid.put(entry.getKey(), " 2 points " + cn);
|
invalid.put(entry.getKey(), " 2 points " + cn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
#Generated by Maven
|
|
||||||
#Sun Oct 22 00:34:49 EDT 2017
|
|
||||||
version=1.4.26
|
|
||||||
groupId=org.mage
|
|
||||||
artifactId=mage-game-pennydreadfulcommanderfreeforall
|
|
|
@ -41,7 +41,9 @@ import mage.constants.Zone;
|
||||||
import mage.filter.FilterCard;
|
import mage.filter.FilterCard;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
import mage.target.Target;
|
||||||
import mage.target.TargetCard;
|
import mage.target.TargetCard;
|
||||||
|
import mage.target.common.TargetOpponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author North
|
* @author North
|
||||||
|
@ -96,6 +98,13 @@ class FactOrFictionEffect extends OneShotEffect {
|
||||||
Set<UUID> opponents = game.getOpponents(source.getControllerId());
|
Set<UUID> opponents = game.getOpponents(source.getControllerId());
|
||||||
if (!opponents.isEmpty()) {
|
if (!opponents.isEmpty()) {
|
||||||
Player opponent = game.getPlayer(opponents.iterator().next());
|
Player opponent = game.getPlayer(opponents.iterator().next());
|
||||||
|
if (opponents.size() > 1) {
|
||||||
|
Target targetOpponent = new TargetOpponent(true);
|
||||||
|
if (controller.chooseTarget(Outcome.Neutral, targetOpponent, source, game)) {
|
||||||
|
opponent = game.getPlayer(targetOpponent.getFirstTarget());
|
||||||
|
game.informPlayers(controller.getLogName() + " chose " + opponent.getLogName() + " to separate the revealed cards");
|
||||||
|
}
|
||||||
|
}
|
||||||
TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, new FilterCard("cards to put in the first pile"));
|
TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, new FilterCard("cards to put in the first pile"));
|
||||||
List<Card> pile1 = new ArrayList<>();
|
List<Card> pile1 = new ArrayList<>();
|
||||||
if (opponent.choose(Outcome.Neutral, cards, target, game)) {
|
if (opponent.choose(Outcome.Neutral, cards, target, game)) {
|
||||||
|
|
|
@ -99,7 +99,13 @@ class HubrisReturnEffect extends OneShotEffect {
|
||||||
for (UUID targetId : targetPointer.getTargets(game, source)) {
|
for (UUID targetId : targetPointer.getTargets(game, source)) {
|
||||||
Permanent creature = game.getPermanent(targetId);
|
Permanent creature = game.getPermanent(targetId);
|
||||||
if (creature != null) {
|
if (creature != null) {
|
||||||
Cards cardsToHand = new CardsImpl(creature.getAttachments());
|
Cards cardsToHand = new CardsImpl();
|
||||||
|
for (UUID cardId : creature.getAttachments()) {
|
||||||
|
Permanent card = game.getPermanent(cardId);
|
||||||
|
if (card != null && card.hasSubtype(SubType.AURA, game)) {
|
||||||
|
cardsToHand.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
cardsToHand.add(creature);
|
cardsToHand.add(creature);
|
||||||
controller.moveCards(cardsToHand, Zone.HAND, source, game);
|
controller.moveCards(cardsToHand, Zone.HAND, source, game);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,8 +48,10 @@ import mage.constants.Outcome;
|
||||||
import mage.constants.Duration;
|
import mage.constants.Duration;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.counters.BoostCounter;
|
import mage.counters.BoostCounter;
|
||||||
|
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.filter.predicate.Predicates;
|
import mage.filter.predicate.Predicates;
|
||||||
|
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||||
import mage.filter.predicate.permanent.TappedPredicate;
|
import mage.filter.predicate.permanent.TappedPredicate;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.target.common.TargetControlledCreaturePermanent;
|
import mage.target.common.TargetControlledCreaturePermanent;
|
||||||
|
@ -61,6 +63,12 @@ import mage.target.common.TargetCreatureOrPlayer;
|
||||||
*/
|
*/
|
||||||
public class KrovikanPlague extends CardImpl {
|
public class KrovikanPlague extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterControlledCreaturePermanent filterNonWall = new FilterControlledCreaturePermanent("non-Wall creature you control");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filterNonWall.add(Predicates.not(new SubtypePredicate(SubType.WALL)));
|
||||||
|
}
|
||||||
|
|
||||||
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("enchanted creature is untapped");
|
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("enchanted creature is untapped");
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -71,8 +79,8 @@ public class KrovikanPlague extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}");
|
||||||
this.subtype.add(SubType.AURA);
|
this.subtype.add(SubType.AURA);
|
||||||
|
|
||||||
// Enchant creature you control
|
// Enchant non-Wall creature you control
|
||||||
TargetPermanent auraTarget = new TargetControlledCreaturePermanent();
|
TargetPermanent auraTarget = new TargetControlledCreaturePermanent(filterNonWall);
|
||||||
this.getSpellAbility().addTarget(auraTarget);
|
this.getSpellAbility().addTarget(auraTarget);
|
||||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
|
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
|
||||||
Ability ability = new EnchantAbility(auraTarget.getTargetName());
|
Ability ability = new EnchantAbility(auraTarget.getTargetName());
|
||||||
|
|
|
@ -32,6 +32,7 @@ import mage.abilities.Ability;
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
import mage.abilities.costs.common.PayLifeCost;
|
import mage.abilities.costs.common.PayLifeCost;
|
||||||
import mage.abilities.effects.common.DestroyMultiTargetEffect;
|
import mage.abilities.effects.common.DestroyMultiTargetEffect;
|
||||||
|
import mage.abilities.effects.common.InfoEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
|
@ -46,13 +47,13 @@ import mage.target.common.TargetCreaturePermanent;
|
||||||
public class PhyrexianPurge extends CardImpl {
|
public class PhyrexianPurge extends CardImpl {
|
||||||
|
|
||||||
public PhyrexianPurge(UUID ownerId, CardSetInfo setInfo) {
|
public PhyrexianPurge(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{R}");
|
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{R}");
|
||||||
|
|
||||||
// Destroy any number of target creatures.
|
// Destroy any number of target creatures.
|
||||||
// Phyrexian Purge costs 3 life more to cast for each target.
|
// Phyrexian Purge costs 3 life more to cast for each target.
|
||||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE));
|
this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE));
|
||||||
this.getSpellAbility().addEffect(new DestroyMultiTargetEffect());
|
this.getSpellAbility().addEffect(new DestroyMultiTargetEffect());
|
||||||
|
this.getSpellAbility().addEffect(new InfoEffect("<br><br>{this} costs 3 life more to cast for each target"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -29,18 +29,24 @@ package mage.cards.r;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
|
import mage.MageObject;
|
||||||
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.effects.common.CantBeCounteredControlledEffect;
|
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
|
||||||
import mage.abilities.effects.common.CantBeCounteredSourceEffect;
|
import mage.abilities.effects.common.CantBeCounteredSourceEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Duration;
|
import mage.constants.Duration;
|
||||||
|
import mage.constants.Outcome;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.filter.FilterSpell;
|
import mage.filter.FilterSpell;
|
||||||
import mage.filter.FilterStackObject;
|
|
||||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.events.GameEvent;
|
||||||
|
import mage.game.stack.Spell;
|
||||||
|
import mage.game.stack.StackObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -54,9 +60,8 @@ public class RootSliver extends CardImpl {
|
||||||
filter.add(new SubtypePredicate(SubType.SLIVER));
|
filter.add(new SubtypePredicate(SubType.SLIVER));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public RootSliver(UUID ownerId, CardSetInfo setInfo) {
|
public RootSliver(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}");
|
||||||
this.subtype.add(SubType.SLIVER);
|
this.subtype.add(SubType.SLIVER);
|
||||||
|
|
||||||
this.power = new MageInt(2);
|
this.power = new MageInt(2);
|
||||||
|
@ -65,7 +70,7 @@ public class RootSliver extends CardImpl {
|
||||||
// Root Sliver can't be countered.
|
// Root Sliver can't be countered.
|
||||||
this.addAbility(new SimpleStaticAbility(Zone.STACK, new CantBeCounteredSourceEffect()));
|
this.addAbility(new SimpleStaticAbility(Zone.STACK, new CantBeCounteredSourceEffect()));
|
||||||
// Sliver spells can't be countered by spells or abilities.
|
// Sliver spells can't be countered by spells or abilities.
|
||||||
this.addAbility(new SimpleStaticAbility(Zone.STACK, new CantBeCounteredControlledEffect(filter, new FilterStackObject(), Duration.WhileOnBattlefield)));
|
this.addAbility(new SimpleStaticAbility(Zone.STACK, new RootSliverEffect()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,3 +84,42 @@ public class RootSliver extends CardImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RootSliverEffect extends ContinuousRuleModifyingEffectImpl {
|
||||||
|
|
||||||
|
public RootSliverEffect() {
|
||||||
|
super(Duration.WhileOnBattlefield, Outcome.Benefit);
|
||||||
|
staticText = "Sliver spells can't be countered by spells or abilities.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public RootSliverEffect(final RootSliverEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RootSliverEffect copy() {
|
||||||
|
return new RootSliverEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checksEventType(GameEvent event, Game game) {
|
||||||
|
return event.getType() == GameEvent.EventType.COUNTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||||
|
Spell spell = game.getStack().getSpell(event.getTargetId());
|
||||||
|
if (spell != null && spell.hasSubtype(SubType.SLIVER, game)) {
|
||||||
|
MageObject sourceObject = game.getObject(event.getSourceId());
|
||||||
|
if (sourceObject != null && sourceObject instanceof StackObject) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
|
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
|
||||||
import mage.abilities.costs.common.SacrificeTargetCost;
|
import mage.abilities.costs.common.SacrificeTargetCost;
|
||||||
|
import mage.abilities.effects.common.DoIfCostPaid;
|
||||||
import mage.abilities.effects.common.turn.AddExtraTurnControllerEffect;
|
import mage.abilities.effects.common.turn.AddExtraTurnControllerEffect;
|
||||||
import mage.abilities.keyword.ChampionAbility;
|
import mage.abilities.keyword.ChampionAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
|
@ -48,7 +49,7 @@ import mage.target.common.TargetControlledPermanent;
|
||||||
*/
|
*/
|
||||||
public class WanderwineProphets extends CardImpl {
|
public class WanderwineProphets extends CardImpl {
|
||||||
|
|
||||||
private static final FilterControlledPermanent filter = new FilterControlledPermanent("Merfolk");
|
private static final FilterControlledPermanent filter = new FilterControlledPermanent("a Merfolk");
|
||||||
|
|
||||||
static {
|
static {
|
||||||
filter.add(new SubtypePredicate(SubType.MERFOLK));
|
filter.add(new SubtypePredicate(SubType.MERFOLK));
|
||||||
|
@ -64,8 +65,10 @@ public class WanderwineProphets extends CardImpl {
|
||||||
// Champion a Merfolk
|
// Champion a Merfolk
|
||||||
this.addAbility(new ChampionAbility(this, SubType.MERFOLK, false));
|
this.addAbility(new ChampionAbility(this, SubType.MERFOLK, false));
|
||||||
// Whenever Wanderwine Prophets deals combat damage to a player, you may sacrifice a Merfolk. If you do, take an extra turn after this one.
|
// Whenever Wanderwine Prophets deals combat damage to a player, you may sacrifice a Merfolk. If you do, take an extra turn after this one.
|
||||||
Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new AddExtraTurnControllerEffect(), true);
|
Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DoIfCostPaid(
|
||||||
ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(1, 1, filter, true)));
|
new AddExtraTurnControllerEffect(),
|
||||||
|
new SacrificeTargetCost(new TargetControlledPermanent(1, 1, filter, true))
|
||||||
|
), true);
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class AtTheBeginOfNextEndStepDelayedTriggeredAbility extends DelayedTrigg
|
||||||
}
|
}
|
||||||
|
|
||||||
public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone zone, Effect effect, TargetController targetController, Condition condition) {
|
public AtTheBeginOfNextEndStepDelayedTriggeredAbility(Zone zone, Effect effect, TargetController targetController, Condition condition) {
|
||||||
super(effect, Duration.EndOfTurn);
|
super(effect, Duration.Custom);
|
||||||
this.zone = zone;
|
this.zone = zone;
|
||||||
this.targetController = targetController;
|
this.targetController = targetController;
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
|
|
15
Utils/cut.pl
15
Utils/cut.pl
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/perl
|
#!/usr/bin/perl
|
||||||
##
|
##
|
||||||
# File : get_all.pl
|
# File : cut.pl
|
||||||
# Author : spjspj
|
# Author : spjspj
|
||||||
##
|
##
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ use POSIX qw(strftime);
|
||||||
print (" cut.pl full_text.txt keys 0 filegrep\n");
|
print (" cut.pl full_text.txt keys 0 filegrep\n");
|
||||||
print (" cut.pl full_text.txt 0 0 make_code_bat\n");
|
print (" cut.pl full_text.txt 0 0 make_code_bat\n");
|
||||||
print (" dir /a /b /s *.java | cut.pl stdin 0 0 make_code_bat > bob.bat\n");
|
print (" dir /a /b /s *.java | cut.pl stdin 0 0 make_code_bat > bob.bat\n");
|
||||||
|
print (" dir /a /b /s *.xml | cut.pl stdin 0 0 make_code_bat > bob_xml.bat\n");
|
||||||
print (" cut.pl d:\\perl_programs output.*txt 7 age_dir | cut.pl list . 0 grep\n");
|
print (" cut.pl d:\\perl_programs output.*txt 7 age_dir | cut.pl list . 0 grep\n");
|
||||||
print (" cut.pl bob.txt 0 0 uniquelines \n");
|
print (" cut.pl bob.txt 0 0 uniquelines \n");
|
||||||
print (" cut.pl file 0 0 strip_http\n");
|
print (" cut.pl file 0 0 strip_http\n");
|
||||||
|
@ -330,6 +331,8 @@ use POSIX qw(strftime);
|
||||||
if ($operation eq "wget_image")
|
if ($operation eq "wget_image")
|
||||||
{
|
{
|
||||||
my $i;
|
my $i;
|
||||||
|
{
|
||||||
|
if (!(-f "$helper"))
|
||||||
{
|
{
|
||||||
my $url = $term;
|
my $url = $term;
|
||||||
print ("Download :$url:\n");
|
print ("Download :$url:\n");
|
||||||
|
@ -342,6 +345,15 @@ use POSIX qw(strftime);
|
||||||
close OUTPUT;
|
close OUTPUT;
|
||||||
print $url, " >>> ", $helper, "\n";
|
print $url, " >>> ", $helper, "\n";
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print ("Found $helper existed already..\n");
|
||||||
|
if (-s "$helper" == 0)
|
||||||
|
{
|
||||||
|
`del "$helper"`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($operation eq "grep")
|
if ($operation eq "grep")
|
||||||
{
|
{
|
||||||
|
@ -898,5 +910,4 @@ use POSIX qw(strftime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
281
Utils/get_modo_artids.pl
Normal file
281
Utils/get_modo_artids.pl
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
##
|
||||||
|
# File : get_modo_artids.pl
|
||||||
|
# Date : 12/Nov/2017
|
||||||
|
# Author : spjspj
|
||||||
|
# Purpose : Get the artid for each face art of each card from modo (mtgo)
|
||||||
|
# this then goes into some form of image such as:
|
||||||
|
# http://mtgoclientdepot.onlinegaming.wizards.com/Graphics/Cards/Pics/161922_typ_reg_sty_050.jpg
|
||||||
|
# "" /00100_typ_reg_sty_001.jpg
|
||||||
|
# "" /01440_typ_reg_sty_010.jpg
|
||||||
|
# "" /102392_typ_reg_sty_010.jpg
|
||||||
|
# "" /400603_typ_reg_sty_050.jpg
|
||||||
|
# This program expects that you've copied the relevant .xml files from an installation of mtgo
|
||||||
|
# into the current working directory for this perl script:
|
||||||
|
# For example: CARDNAME_STRING.xml, client_BNG.xml,
|
||||||
|
# Also, it expects to be running on windows
|
||||||
|
##
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use LWP::Simple;
|
||||||
|
use POSIX qw(strftime);
|
||||||
|
|
||||||
|
|
||||||
|
# From xmage code - fix from MODO to XMAGE type set codes
|
||||||
|
my %fix_set_codes;
|
||||||
|
$fix_set_codes {"2U"} = "2ED";
|
||||||
|
$fix_set_codes {"3E"} = "3ED";
|
||||||
|
$fix_set_codes {"4E"} = "4ED";
|
||||||
|
$fix_set_codes {"5E"} = "5ED";
|
||||||
|
$fix_set_codes {"6E"} = "6ED";
|
||||||
|
$fix_set_codes {"7E"} = "7ED";
|
||||||
|
$fix_set_codes {"AL"} = "ALL";
|
||||||
|
$fix_set_codes {"AP"} = "APC";
|
||||||
|
$fix_set_codes {"AN"} = "ARN";
|
||||||
|
$fix_set_codes {"AQ"} = "ATQ";
|
||||||
|
$fix_set_codes {"CM1"} = "CMA";
|
||||||
|
$fix_set_codes {"DD3_DVD"} = "DD3DVD";
|
||||||
|
$fix_set_codes {"DD3_EVG"} = "DD3EVG";
|
||||||
|
$fix_set_codes {"DD3_GLV"} = "DD3GLV";
|
||||||
|
$fix_set_codes {"DD3_JVC"} = "DD3JVC";
|
||||||
|
$fix_set_codes {"DK"} = "DRK";
|
||||||
|
$fix_set_codes {"EX"} = "EXO";
|
||||||
|
$fix_set_codes {"FE"} = "FEM";
|
||||||
|
$fix_set_codes {"HM"} = "HML";
|
||||||
|
$fix_set_codes {"IA"} = "ICE";
|
||||||
|
$fix_set_codes {"IN"} = "INV";
|
||||||
|
$fix_set_codes {"1E"} = "LEA";
|
||||||
|
$fix_set_codes {"2E"} = "LEB";
|
||||||
|
$fix_set_codes {"LE"} = "LEG";
|
||||||
|
$fix_set_codes {"MI"} = "MIR";
|
||||||
|
$fix_set_codes {"MM"} = "MMQ";
|
||||||
|
$fix_set_codes {"MPS_KLD"} = "MPS";
|
||||||
|
$fix_set_codes {"NE"} = "NEM";
|
||||||
|
$fix_set_codes {"NE"} = "NMS";
|
||||||
|
$fix_set_codes {"OD"} = "ODY";
|
||||||
|
$fix_set_codes {"PR"} = "PCY";
|
||||||
|
$fix_set_codes {"PS"} = "PLS";
|
||||||
|
$fix_set_codes {"P2"} = "PO2";
|
||||||
|
$fix_set_codes {"PO"} = "POR";
|
||||||
|
$fix_set_codes {"PK"} = "PTK";
|
||||||
|
$fix_set_codes {"ST"} = "STH";
|
||||||
|
$fix_set_codes {"TE"} = "TMP";
|
||||||
|
$fix_set_codes {"CG"} = "UDS";
|
||||||
|
$fix_set_codes {"UD"} = "UDS";
|
||||||
|
$fix_set_codes {"GU"} = "ULG";
|
||||||
|
$fix_set_codes {"UZ"} = "USG";
|
||||||
|
$fix_set_codes {"VI"} = "VIS";
|
||||||
|
$fix_set_codes {"WL"} = "WTH";
|
||||||
|
|
||||||
|
# Main
|
||||||
|
{
|
||||||
|
my $card = $ARGV [0];
|
||||||
|
|
||||||
|
# Example: <CARDNAME_STRING_ITEM id='ID258_461'>Abandoned Sarcophagus</CARDNAME_STRING_ITEM>
|
||||||
|
my $vals = `find /I "CARDNAME" *CARDNAME*STR*`;
|
||||||
|
my %ids;
|
||||||
|
my %set_names;
|
||||||
|
my $count = 0;
|
||||||
|
while ($vals =~ s/^.*CARDNAME_STRING_ITEM id='(.*?)'>(.*?)<\/CARDNAME_STRING.*\n//im)
|
||||||
|
{
|
||||||
|
$ids {$1} = $2;
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
print ("Finished reading $count names\n");
|
||||||
|
|
||||||
|
|
||||||
|
#$vals = `find /I "<" *lient*`;
|
||||||
|
$vals = `findstr /I "CARDNAME_STRING DIGITALOBJECT ARTID CLONE FRAMESTYLE " *lient* | find /I /V "_DO.xml"`;
|
||||||
|
|
||||||
|
my $current_artid = "";
|
||||||
|
my $current_clone_id = "";
|
||||||
|
my $current_doc_id = "";
|
||||||
|
my $current_line = "";
|
||||||
|
my $current_name = "";
|
||||||
|
my $current_set = "";
|
||||||
|
my $num_set = 1;
|
||||||
|
my %docid_to_clone;
|
||||||
|
|
||||||
|
while ($vals =~ s/^(.*)\n//im)
|
||||||
|
{
|
||||||
|
my $line = $1;
|
||||||
|
$line =~ m/^client_(.*)?\.xml/;
|
||||||
|
$current_set = $1;
|
||||||
|
|
||||||
|
#<DigitalObject DigitalObjectCatalogID='DOC_26588'> *** IN
|
||||||
|
# <ARTID value='100691'/> *** IN
|
||||||
|
# <ARTIST_NAME_STRING id='ID257_266'/>
|
||||||
|
# <CARDNAME_STRING id='ID258_12464'/> *** IN
|
||||||
|
# <CARDNAME_TOKEN id='ID259_12464'/>
|
||||||
|
# <CARDSETNAME_STRING id='ID516_103'/>
|
||||||
|
# <CARDTEXTURE_NUMBER value='53176'/>
|
||||||
|
# <COLLECTOR_INFO_STRING value='150/180'/>
|
||||||
|
# <COLOR id='ID517_6'/>
|
||||||
|
# <COLOR_IDENTITY id='ID518_4'/>
|
||||||
|
# <CONVERTED_MANA_COST id='ID520_3'/>
|
||||||
|
# <CREATURE_TYPE_STRING0 id='ID777_71'/>
|
||||||
|
# <CREATURE_TYPEID0 id='ID521_12'/>
|
||||||
|
# <FRAMESTYLE value='7'/>
|
||||||
|
# <IS_CREATURE value='1'/>
|
||||||
|
# <MANA_COST_STRING id='ID1811_114'/>
|
||||||
|
# <POWER value='4'/>
|
||||||
|
# <POWERTOUGHNESS_STRING value='4/5'/>
|
||||||
|
# <RARITY_STATUS id='ID1818_3'/>
|
||||||
|
# <REAL_ORACLETEXT_STRING id='ID1819_11686'/>
|
||||||
|
# <SHOULDWORK value='1'/>
|
||||||
|
# <SHROUD value='1'/>
|
||||||
|
# <TOUGHNESS value='5'/>
|
||||||
|
# <WATERMARK value='0'/>
|
||||||
|
#</DigitalObject>
|
||||||
|
|
||||||
|
if ($line =~ m/<DigitalObject DigitalObjectCatalogID='([^']+)'/)
|
||||||
|
{
|
||||||
|
$current_line = "$current_set;$1;";
|
||||||
|
$current_doc_id = $1;
|
||||||
|
}
|
||||||
|
if ($line =~ m/\s*<ARTID value='(\d+)'/)
|
||||||
|
{
|
||||||
|
$current_line .= "artid=$1;";
|
||||||
|
}
|
||||||
|
if ($line =~ m/CARDNAME_STRING id='([^']+)'/)
|
||||||
|
{
|
||||||
|
$current_line = "$ids{$1};$current_line;($1)";
|
||||||
|
$current_name = "$ids{$1}";
|
||||||
|
}
|
||||||
|
if ($line =~ m/CLONE_ID value='([^']+)'/)
|
||||||
|
{
|
||||||
|
my $clone_id = $1;
|
||||||
|
$current_line .= ";Clone=($clone_id)";
|
||||||
|
$current_clone_id = $clone_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($line =~ m/<\/DigitalObject/)
|
||||||
|
{
|
||||||
|
if ($current_name =~ m/.+/ && $current_doc_id =~ m/.+/)
|
||||||
|
{
|
||||||
|
$docid_to_clone {$current_doc_id} = $current_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_line = "";
|
||||||
|
$current_name = "";
|
||||||
|
$current_clone_id = "";
|
||||||
|
$current_doc_id = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run it again..
|
||||||
|
$vals = `findstr /I "CARDNAME_STRING DIGITALOBJECT ARTID CLONE FRAMESTYLE" *lient* | find /I /V "_DO.xml"`;
|
||||||
|
|
||||||
|
$current_set = "";
|
||||||
|
$num_set = 1;
|
||||||
|
$current_artid = "";
|
||||||
|
$current_clone_id = "";
|
||||||
|
$current_doc_id = "";
|
||||||
|
$current_line = "";
|
||||||
|
$current_name = "";
|
||||||
|
my %seen_artids;
|
||||||
|
my $current_framestyle = "";
|
||||||
|
|
||||||
|
my %framestyles;
|
||||||
|
$framestyles {1} = "001";
|
||||||
|
$framestyles {3} = "010";
|
||||||
|
$framestyles {31} = "010";
|
||||||
|
$framestyles {11} = "010";
|
||||||
|
$framestyles {14} = "010";
|
||||||
|
$framestyles {15} = "010";
|
||||||
|
|
||||||
|
while ($vals =~ s/^(.*)\n//im)
|
||||||
|
{
|
||||||
|
my $line = $1;
|
||||||
|
$line =~ m/^client_(.*)?\.xml/;
|
||||||
|
$current_set = $1;
|
||||||
|
if (defined ($fix_set_codes {$current_set}))
|
||||||
|
{
|
||||||
|
$current_set = $fix_set_codes {$current_set};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($line =~ m/<DigitalObject DigitalObjectCatalogID='([^']+)'/)
|
||||||
|
{
|
||||||
|
$current_line = "$current_set;$1;";
|
||||||
|
$current_doc_id = $1;
|
||||||
|
}
|
||||||
|
if ($line =~ m/\s*<ARTID value='(\d+)'/)
|
||||||
|
{
|
||||||
|
$current_line .= "artid=$1;";
|
||||||
|
$current_artid = $1;
|
||||||
|
}
|
||||||
|
if ($line =~ m/CARDNAME_STRING id='([^']+)'/)
|
||||||
|
{
|
||||||
|
$current_line = "$ids{$1};$current_line;($1)";
|
||||||
|
$current_name = "$ids{$1}";
|
||||||
|
}
|
||||||
|
if ($line =~ m/CLONE_ID value='([^']+)'/)
|
||||||
|
{
|
||||||
|
my $clone_id = $1;
|
||||||
|
$current_line .= ";Clone=($clone_id)";
|
||||||
|
$current_clone_id = $clone_id;
|
||||||
|
}
|
||||||
|
if ($line =~ m/FRAMESTYLE value='([^']+)'/)
|
||||||
|
{
|
||||||
|
$current_framestyle = "$1";
|
||||||
|
$current_framestyle = $framestyles {$current_framestyle};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($line =~ m/<\/DigitalObject/)
|
||||||
|
{
|
||||||
|
if ($docid_to_clone {$current_doc_id} =~ m/.+/)
|
||||||
|
{
|
||||||
|
$current_name = "$docid_to_clone{$current_doc_id}";
|
||||||
|
}
|
||||||
|
if ($current_name =~ m/.+/ && $current_doc_id =~ m/.+/)
|
||||||
|
{
|
||||||
|
$docid_to_clone {$current_doc_id} = $current_name;
|
||||||
|
}
|
||||||
|
if ($current_name =~ m/^$/)
|
||||||
|
{
|
||||||
|
$current_name = $docid_to_clone {$current_clone_id};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($current_name =~ m/..*/ && $current_set =~ m/..*/ && $current_artid =~ m/..*/)
|
||||||
|
{
|
||||||
|
print ("$current_name;$current_set;$current_artid\n");
|
||||||
|
# cut.pl statement..
|
||||||
|
# echo "1" | cut.pl stdin "http://mtgoclientdepot.onlinegaming.wizards.com/Graphics/Cards/Pics/00010_typ_reg_sty_001.jpg" "ME4\Badlands.jpg" wget_image
|
||||||
|
if (!defined ($seen_artids {$current_artid}))
|
||||||
|
{
|
||||||
|
$seen_artids {$current_artid} = "$current_set\\$current_name.jpg";
|
||||||
|
if ($current_artid < 10)
|
||||||
|
{
|
||||||
|
print (" echo \"1\" | cut.pl stdin \"http://mtgoclientdepot.onlinegaming.wizards.com/Graphics/Cards/Pics/0000$current_artid" . "_typ_reg_sty_$current_framestyle.jpg\" \"$current_set\\$current_name.jpg\" wget_image\n");
|
||||||
|
}
|
||||||
|
if ($current_artid < 100)
|
||||||
|
{
|
||||||
|
print (" echo \"1\" | cut.pl stdin \"http://mtgoclientdepot.onlinegaming.wizards.com/Graphics/Cards/Pics/000$current_artid" . "_typ_reg_sty_$current_framestyle.jpg\" \"$current_set\\$current_name.jpg\" wget_image\n");
|
||||||
|
}
|
||||||
|
elsif ($current_artid < 1000)
|
||||||
|
{
|
||||||
|
print (" echo \"1\" | cut.pl stdin \"http://mtgoclientdepot.onlinegaming.wizards.com/Graphics/Cards/Pics/00$current_artid" . "_typ_reg_sty_$current_framestyle.jpg\" \"$current_set\\$current_name.jpg\" wget_image\n");
|
||||||
|
}
|
||||||
|
elsif ($current_artid < 10000)
|
||||||
|
{
|
||||||
|
print (" echo \"1\" | cut.pl stdin \"http://mtgoclientdepot.onlinegaming.wizards.com/Graphics/Cards/Pics/0$current_artid" . "_typ_reg_sty_$current_framestyle.jpg\" \"$current_set\\$current_name.jpg\" wget_image\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print (" echo \"1\" | cut.pl stdin \"http://mtgoclientdepot.onlinegaming.wizards.com/Graphics/Cards/Pics/$current_artid" . "_typ_reg_sty_$current_framestyle.jpg\" \"$current_set\\$current_name.jpg\" wget_image\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print (" copy \"$seen_artids{$current_artid}\" \"$current_set\\$current_name.jpg\"\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_artid = "";
|
||||||
|
$current_clone_id = "";
|
||||||
|
$current_doc_id = "";
|
||||||
|
$current_line = "";
|
||||||
|
$current_name = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31904
Utils/modo_artids
Normal file
31904
Utils/modo_artids
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue