Working Card Rendering

This commit is contained in:
Mark Langen 2016-08-31 04:43:28 -06:00
parent eeaea4c566
commit d5415d2d04
63 changed files with 17729 additions and 1769 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -277,6 +277,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
try {
UIManager.put("desktop", new Color(0, 0, 0, 0));
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
//UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
// stop JSplitPane from eating F6 and F8 or any other function keys
{
Object value = UIManager.get("SplitPane.ancestorInputMap");

View file

@ -92,6 +92,7 @@ import mage.view.CardView;
import mage.view.CounterView;
import mage.view.PermanentView;
import mage.view.StackAbilityView;
import org.apache.log4j.Logger;
/**
*
@ -191,7 +192,7 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis
gSmall.drawImage(ImageHelper.scaleImage(image, Config.dimensions.frameWidth, Config.dimensions.frameHeight), 0, 0, this);
gImage.setFont(new Font("Arial", Font.PLAIN, NAME_FONT_MAX_SIZE));
gImage.drawString(card.getName(), CONTENT_MAX_XOFFSET, NAME_MAX_YOFFSET);
gImage.drawString(card.getName()+"TEST", CONTENT_MAX_XOFFSET, NAME_MAX_YOFFSET);
if (card.getCardTypes().contains(CardType.CREATURE)) {
gImage.drawString(card.getPower() + "/" + card.getToughness(), POWBOX_TEXT_MAX_LEFT, POWBOX_TEXT_MAX_TOP);
} else if (card.getCardTypes().contains(CardType.PLANESWALKER)) {
@ -205,9 +206,9 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis
gImage.dispose();
gSmall.setFont(new Font("Arial", Font.PLAIN, Config.dimensions.nameFontSize));
gSmall.drawString(card.getName(), Config.dimensions.contentXOffset, Config.dimensions.nameYOffset);
gSmall.drawString(card.getName()+"TEST2", Config.dimensions.contentXOffset, Config.dimensions.nameYOffset);
if (card.getCardTypes().contains(CardType.CREATURE)) {
gSmall.drawString(card.getPower() + "/" + card.getToughness(), Config.dimensions.powBoxTextLeft, Config.dimensions.powBoxTextTop);
gSmall.drawString(card.getPower() + "/-/" + card.getToughness(), Config.dimensions.powBoxTextLeft, Config.dimensions.powBoxTextTop);
} else if (card.getCardTypes().contains(CardType.PLANESWALKER)) {
gSmall.drawString(card.getLoyalty(), Config.dimensions.powBoxTextLeft, Config.dimensions.powBoxTextTop);
}
@ -319,6 +320,19 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis
return sbType.toString();
}
protected void drawDetailed(Graphics2D g) {
// Get the size of the card
int width = getWidth();
int height = getHeight();
g.setColor(Color.black);
g.drawRoundRect(0, 0, width, height, 4, 4);
g.setColor(Color.white);
g.setFont(new Font("Arial", Font.PLAIN, NAME_FONT_MAX_SIZE));
g.drawString(card.getName(), 0, 0);
Logger.getLogger(Card.class).info("Drawing");
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
@ -355,6 +369,8 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis
@Override
public void paintComponent(Graphics graphics) {
drawDetailed((Graphics2D)graphics);
/*
Graphics2D g2 = (Graphics2D) graphics;
g2.drawImage(small, 0, 0, this);
@ -365,6 +381,7 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis
g2.setColor(Color.BLACK);
}
g2.drawRect(0, 0, Config.dimensions.frameWidth - 1, Config.dimensions.frameHeight - 1);
*/
}
@Override

View file

@ -34,21 +34,16 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="panelControl" pref="703" max="32767" attributes="0"/>
<Component id="panelCardArea" max="32767" attributes="0"/>
</Group>
</Group>
<Component id="panelControl" alignment="0" pref="467" max="32767" attributes="0"/>
<Component id="panelCardArea" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="panelControl" min="-2" pref="25" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="panelCardArea" pref="86" max="32767" attributes="0"/>
<Component id="panelControl" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Component id="panelCardArea" pref="179" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -81,15 +76,7 @@
<Component id="lblLandCount" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblCreatureCount" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblSorceryCount" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblInstantCount" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lblEnchantmentCount" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="4" max="-2" attributes="0"/>
<Component id="lblArtifactCount" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="chkPiles" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="cbSortBy" min="-2" max="-2" attributes="0"/>
@ -97,7 +84,7 @@
<Component id="jToggleListView" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jToggleCardView" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="62" max="32767" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -109,11 +96,7 @@
<Component id="lblCount" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblLandCount" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblCreatureCount" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblSorceryCount" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblInstantCount" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblEnchantmentCount" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="chkPiles" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblArtifactCount" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="cbSortBy" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="jToggleListView" min="-2" max="-2" attributes="0"/>
@ -177,60 +160,6 @@
<Property name="verifyInputWhenFocusTarget" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lblSorceryCount">
<Properties>
<Property name="horizontalAlignment" type="int" value="2"/>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/buttons/type_sorcery.png"/>
</Property>
<Property name="text" type="java.lang.String" value="999"/>
<Property name="toolTipText" type="java.lang.String" value="Number of sorceries."/>
<Property name="verticalAlignment" type="int" value="1"/>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="Default Cursor"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="inheritsPopupMenu" type="boolean" value="false"/>
<Property name="requestFocusEnabled" type="boolean" value="false"/>
<Property name="verifyInputWhenFocusTarget" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lblInstantCount">
<Properties>
<Property name="horizontalAlignment" type="int" value="2"/>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/buttons/type_instant.png"/>
</Property>
<Property name="text" type="java.lang.String" value="999"/>
<Property name="toolTipText" type="java.lang.String" value="Number of instants."/>
<Property name="verticalAlignment" type="int" value="1"/>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="Default Cursor"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="inheritsPopupMenu" type="boolean" value="false"/>
<Property name="requestFocusEnabled" type="boolean" value="false"/>
<Property name="verifyInputWhenFocusTarget" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lblEnchantmentCount">
<Properties>
<Property name="horizontalAlignment" type="int" value="2"/>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/buttons/type_enchantment.png"/>
</Property>
<Property name="text" type="java.lang.String" value="999"/>
<Property name="toolTipText" type="java.lang.String" value="Number of enchantments."/>
<Property name="verticalAlignment" type="int" value="1"/>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="Default Cursor"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="inheritsPopupMenu" type="boolean" value="false"/>
<Property name="requestFocusEnabled" type="boolean" value="false"/>
<Property name="verifyInputWhenFocusTarget" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="chkPiles">
<Properties>
<Property name="text" type="java.lang.String" value="Piles"/>
@ -324,24 +253,6 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jToggleCardViewActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblArtifactCount">
<Properties>
<Property name="horizontalAlignment" type="int" value="2"/>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/buttons/type_artifact.png"/>
</Property>
<Property name="text" type="java.lang.String" value="999"/>
<Property name="toolTipText" type="java.lang.String" value="Number of artifacts"/>
<Property name="verticalAlignment" type="int" value="1"/>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="Default Cursor"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="inheritsPopupMenu" type="boolean" value="false"/>
<Property name="requestFocusEnabled" type="boolean" value="false"/>
<Property name="verifyInputWhenFocusTarget" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="panelCardArea">

View file

@ -211,7 +211,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
}
});
mainModel.setUpdateCountsCallback(new UpdateCountsCallback(lblCount, lblCreatureCount, lblLandCount, lblSorceryCount, lblInstantCount, lblEnchantmentCount, lblArtifactCount));
mainModel.setUpdateCountsCallback(new UpdateCountsCallback(lblCount, lblCreatureCount, lblLandCount, null, null, null, null));
}
// if you use the deck ediot to build a free deck, numbers can be set directly in deck and sideboard
@ -418,10 +418,6 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
this.lblCount.setText(Integer.toString(count));
this.lblCreatureCount.setText(Integer.toString(creatureCount));
this.lblLandCount.setText(Integer.toString(landCount));
this.lblSorceryCount.setText(Integer.toString(sorceryCount));
this.lblInstantCount.setText(Integer.toString(instantCount));
this.lblEnchantmentCount.setText(Integer.toString(enchantmentCount));
this.lblArtifactCount.setText(Integer.toString(artifactCount));
}
private MageCard addCard(CardView card, BigCard bigCard, UUID gameId) {
@ -482,14 +478,10 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
lblCount = new javax.swing.JLabel();
lblLandCount = new javax.swing.JLabel();
lblCreatureCount = new javax.swing.JLabel();
lblSorceryCount = new javax.swing.JLabel();
lblInstantCount = new javax.swing.JLabel();
lblEnchantmentCount = new javax.swing.JLabel();
chkPiles = new javax.swing.JCheckBox();
cbSortBy = new javax.swing.JComboBox();
jToggleListView = new javax.swing.JToggleButton();
jToggleCardView = new javax.swing.JToggleButton();
lblArtifactCount = new javax.swing.JLabel();
panelCardArea = new javax.swing.JScrollPane();
cardArea = new javax.swing.JLayeredPane();
@ -537,39 +529,6 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
lblCreatureCount.setRequestFocusEnabled(false);
lblCreatureCount.setVerifyInputWhenFocusTarget(false);
lblSorceryCount.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
lblSorceryCount.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/type_sorcery.png"))); // NOI18N
lblSorceryCount.setText("999");
lblSorceryCount.setToolTipText("Number of sorceries.");
lblSorceryCount.setVerticalAlignment(javax.swing.SwingConstants.TOP);
lblSorceryCount.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
lblSorceryCount.setFocusable(false);
lblSorceryCount.setInheritsPopupMenu(false);
lblSorceryCount.setRequestFocusEnabled(false);
lblSorceryCount.setVerifyInputWhenFocusTarget(false);
lblInstantCount.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
lblInstantCount.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/type_instant.png"))); // NOI18N
lblInstantCount.setText("999");
lblInstantCount.setToolTipText("Number of instants.");
lblInstantCount.setVerticalAlignment(javax.swing.SwingConstants.TOP);
lblInstantCount.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
lblInstantCount.setFocusable(false);
lblInstantCount.setInheritsPopupMenu(false);
lblInstantCount.setRequestFocusEnabled(false);
lblInstantCount.setVerifyInputWhenFocusTarget(false);
lblEnchantmentCount.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
lblEnchantmentCount.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/type_enchantment.png"))); // NOI18N
lblEnchantmentCount.setText("999");
lblEnchantmentCount.setToolTipText("Number of enchantments.");
lblEnchantmentCount.setVerticalAlignment(javax.swing.SwingConstants.TOP);
lblEnchantmentCount.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
lblEnchantmentCount.setFocusable(false);
lblEnchantmentCount.setInheritsPopupMenu(false);
lblEnchantmentCount.setRequestFocusEnabled(false);
lblEnchantmentCount.setVerifyInputWhenFocusTarget(false);
chkPiles.setText("Piles");
chkPiles.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
chkPiles.setMargin(new java.awt.Insets(3, 2, 2, 2));
@ -619,17 +578,6 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
}
});
lblArtifactCount.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
lblArtifactCount.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/type_artifact.png"))); // NOI18N
lblArtifactCount.setText("999");
lblArtifactCount.setToolTipText("Number of artifacts");
lblArtifactCount.setVerticalAlignment(javax.swing.SwingConstants.TOP);
lblArtifactCount.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
lblArtifactCount.setFocusable(false);
lblArtifactCount.setInheritsPopupMenu(false);
lblArtifactCount.setRequestFocusEnabled(false);
lblArtifactCount.setVerifyInputWhenFocusTarget(false);
javax.swing.GroupLayout panelControlLayout = new javax.swing.GroupLayout(panelControl);
panelControl.setLayout(panelControlLayout);
panelControlLayout.setHorizontalGroup(
@ -641,14 +589,6 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblCreatureCount)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblSorceryCount)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblInstantCount)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblEnchantmentCount)
.addGap(4, 4, 4)
.addComponent(lblArtifactCount)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkPiles)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(cbSortBy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
@ -656,7 +596,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
.addComponent(jToggleListView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jToggleCardView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 62, Short.MAX_VALUE))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
panelControlLayout.setVerticalGroup(
panelControlLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -666,11 +606,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
.addComponent(lblCount)
.addComponent(lblLandCount)
.addComponent(lblCreatureCount)
.addComponent(lblSorceryCount)
.addComponent(lblInstantCount)
.addComponent(lblEnchantmentCount)
.addComponent(chkPiles)
.addComponent(lblArtifactCount))
.addComponent(chkPiles))
.addComponent(cbSortBy, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jToggleListView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jToggleCardView, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
@ -686,21 +622,27 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(1, 1, 1)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(panelControl, javax.swing.GroupLayout.DEFAULT_SIZE, 703, Short.MAX_VALUE)
.addComponent(panelCardArea)))
.addComponent(panelControl, javax.swing.GroupLayout.PREFERRED_SIZE, 467, Short.MAX_VALUE)
.addComponent(panelCardArea)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(panelControl, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)
.addComponent(panelCardArea, javax.swing.GroupLayout.DEFAULT_SIZE, 86, Short.MAX_VALUE))
.addComponent(panelControl, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(2, 2, 2)
.addComponent(panelCardArea, javax.swing.GroupLayout.DEFAULT_SIZE, 179, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
private void jToggleCardViewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jToggleCardViewActionPerformed
currentView = this;
panelCardArea.setViewportView(cardArea);
cbSortBy.setEnabled(true);
chkPiles.setEnabled(true);
PreferencesDialog.saveValue(PreferencesDialog.KEY_DRAFT_VIEW, "cardView");
redrawCards();
}//GEN-LAST:event_jToggleCardViewActionPerformed
private void jToggleListViewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jToggleListViewActionPerformed
currentView = mainModel;
panelCardArea.setViewportView(mainTable);
@ -720,15 +662,6 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
drawCards(sortSetting);
}//GEN-LAST:event_chkPilesActionPerformed
private void jToggleCardViewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jToggleCardViewActionPerformed
currentView = this;
panelCardArea.setViewportView(cardArea);
cbSortBy.setEnabled(true);
chkPiles.setEnabled(true);
PreferencesDialog.saveValue(PreferencesDialog.KEY_DRAFT_VIEW, "cardView");
redrawCards();
}//GEN-LAST:event_jToggleCardViewActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.ButtonGroup bgView;
private javax.swing.JLayeredPane cardArea;
@ -736,13 +669,9 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
private javax.swing.JCheckBox chkPiles;
private javax.swing.JToggleButton jToggleCardView;
private javax.swing.JToggleButton jToggleListView;
private javax.swing.JLabel lblArtifactCount;
private javax.swing.JLabel lblCount;
private javax.swing.JLabel lblCreatureCount;
private javax.swing.JLabel lblEnchantmentCount;
private javax.swing.JLabel lblInstantCount;
private javax.swing.JLabel lblLandCount;
private javax.swing.JLabel lblSorceryCount;
private javax.swing.JScrollPane panelCardArea;
private javax.swing.JPanel panelControl;
// End of variables declaration//GEN-END:variables

View file

@ -16,31 +16,26 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="deckAreaSplitPane" alignment="0" pref="740" max="32767" attributes="0"/>
<Component id="deckAreaSplitPane" alignment="0" pref="918" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="deckAreaSplitPane" alignment="0" pref="568" max="32767" attributes="0"/>
<Component id="deckAreaSplitPane" alignment="0" pref="377" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="deckAreaSplitPane">
<Properties>
<Property name="orientation" type="int" value="0"/>
<Property name="resizeWeight" type="double" value="0.8"/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="resizeWeight" type="double" value="0.6"/>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Component class="mage.client.cards.CardsList" name="deckList">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
</Component>
<Component class="mage.client.cards.CardsList" name="sideboardList">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
@ -48,6 +43,13 @@
</Constraint>
</Constraints>
</Component>
<Component class="mage.client.cards.CardsList" name="deckList">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>

View file

@ -118,23 +118,23 @@ public class DeckArea extends javax.swing.JPanel {
private void initComponents() {
deckAreaSplitPane = new javax.swing.JSplitPane();
deckList = new mage.client.cards.CardsList();
sideboardList = new mage.client.cards.CardsList();
deckList = new mage.client.cards.CardsList();
deckAreaSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
deckAreaSplitPane.setResizeWeight(0.8);
deckAreaSplitPane.setLeftComponent(deckList);
deckAreaSplitPane.setBorder(null);
deckAreaSplitPane.setResizeWeight(0.6);
deckAreaSplitPane.setRightComponent(sideboardList);
deckAreaSplitPane.setLeftComponent(deckList);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(deckAreaSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 740, Short.MAX_VALUE)
.addComponent(deckAreaSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 918, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(deckAreaSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 568, Short.MAX_VALUE)
.addComponent(deckAreaSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 377, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents

View file

@ -11,7 +11,7 @@ public class UpdateCountsCallback {
private final javax.swing.JLabel lblCount;
private final javax.swing.JLabel lblCreatureCount;
private final javax.swing.JLabel lblLandCount;
private final javax.swing.JLabel lblSoerceryCount;
private final javax.swing.JLabel lblSorceryCount;
private final javax.swing.JLabel lblInstantCount;
private final javax.swing.JLabel lblEnchantmentCount;
private final javax.swing.JLabel lblArtifactCount;
@ -20,19 +20,26 @@ public class UpdateCountsCallback {
this.lblCount = count;
this.lblCreatureCount = creatures;
this.lblLandCount = lands;
this.lblSoerceryCount = sorceries;
this.lblSorceryCount = sorceries;
this.lblInstantCount = instants;
this.lblEnchantmentCount = enchantments;
this.lblArtifactCount = artifacts;
}
public void update(int count, int creatures, int lands, int sorceries, int instants, int enchantments, int artifacts) {
this.lblCount.setText(Integer.toString(count));
this.lblCreatureCount.setText(Integer.toString(creatures));
this.lblLandCount.setText(Integer.toString(lands));
this.lblSoerceryCount.setText(Integer.toString(sorceries));
this.lblInstantCount.setText(Integer.toString(instants));
this.lblEnchantmentCount.setText(Integer.toString(enchantments));
this.lblArtifactCount.setText(Integer.toString(artifacts));
if (this.lblCount != null)
this.lblCount.setText(Integer.toString(count));
if (this.lblCreatureCount != null)
this.lblCreatureCount.setText(Integer.toString(creatures));
if (this.lblLandCount != null)
this.lblLandCount.setText(Integer.toString(lands));
if (this.lblSorceryCount != null)
this.lblSorceryCount.setText(Integer.toString(sorceries));
if (this.lblInstantCount != null)
this.lblInstantCount.setText(Integer.toString(instants));
if (this.lblEnchantmentCount != null)
this.lblEnchantmentCount.setText(Integer.toString(enchantments));
if (this.lblArtifactCount != null)
this.lblArtifactCount.setText(Integer.toString(artifacts));
}
}

View file

@ -31,7 +31,11 @@
<Component id="exitButton" min="-2" pref="100" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
</Group>
<Component id="tabsPanel" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="tabsPanel" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
@ -146,7 +150,7 @@
<Property name="toolTipText" type="java.lang.String" value="Write the card&apos;s name on the card to make the card name more recognizable."/>
<Property name="actionCommand" type="java.lang.String" value=""/>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="Standardcursor"/>
<Color id="Default Cursor"/>
</Property>
</Properties>
<Events>
@ -3945,7 +3949,7 @@
<Component id="checkBoxEndTurnOthers" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="phases_stopSettings" pref="260" max="32767" attributes="0"/>
<Component id="phases_stopSettings" pref="266" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
@ -4147,11 +4151,15 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="panelCardImages" alignment="0" max="32767" attributes="0"/>
<Component id="panelBackgroundImages" alignment="0" max="32767" attributes="0"/>
<Component id="panelCardImages" max="32767" attributes="0"/>
<Component id="jPanel1" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="panelBackgroundImages" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -4160,11 +4168,13 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="jPanel1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="panelCardImages" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="panelBackgroundImages" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="90" max="32767" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -4189,36 +4199,41 @@
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="24" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="txtImageFolderPath" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnBrowseImageLocation" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="1" max="-2" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="labelNumberOfDownloadThreads" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cbNumberOfDownloadThreads" min="-2" pref="153" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="cbUseDefaultImageFolder" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="cbCheckForNewImages" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="1" max="-2" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="labelPreferedImageLanguage" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="103" alignment="0" groupAlignment="1" max="-2" attributes="0">
<Group type="102" attributes="0">
<Component id="cbCheckForNewImages" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="147" max="-2" attributes="0"/>
</Group>
<Group type="103" alignment="0" groupAlignment="1" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="1" max="-2" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="labelPreferedImageLanguage" max="-2" attributes="0"/>
</Group>
<Component id="cbSaveToZipFiles" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="cbSaveToZipFiles" alignment="0" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
<Component id="cbPreferedImageLanguage" min="-2" pref="153" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="labelNumberOfDownloadThreads" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cbNumberOfDownloadThreads" min="-2" pref="153" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="40" max="-2" attributes="0"/>
<Component id="cbPreferedImageLanguage" min="-2" pref="153" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="cbUseDefaultImageFolder" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="251" max="32767" attributes="0"/>
<EmptySpace min="0" pref="231" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
@ -4228,14 +4243,13 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="cbUseDefaultImageFolder" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="txtImageFolderPath" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnBrowseImageLocation" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="cbCheckForNewImages" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cbSaveToZipFiles" min="-2" max="-2" attributes="0"/>
@ -4249,7 +4263,6 @@
<Component id="cbPreferedImageLanguage" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="labelPreferedImageLanguage" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="48" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -4473,6 +4486,61 @@
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="jPanel1">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Card rendering:">
<Border PropertyName="innerBorder" info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</TitledBorder>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="cbCardRenderImageFallback" min="-2" max="-2" attributes="0"/>
<Component id="cbCardRenderShowReminderText" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="cbCardRenderImageFallback" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cbCardRenderShowReminderText" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="7" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="cbCardRenderImageFallback">
<Properties>
<Property name="text" type="java.lang.String" value="Fall back to plain image based rendering"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbCardRenderImageFallbackActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="cbCardRenderShowReminderText">
<Properties>
<Property name="text" type="java.lang.String" value="Show reminder text in rendered card textboxes"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbCardRenderShowReminderTextActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="tabSounds">
@ -5579,7 +5647,7 @@
<Component id="jLabel17" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace pref="111" max="32767" attributes="0"/>
<EmptySpace pref="91" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>

View file

@ -118,6 +118,9 @@ public class PreferencesDialog extends javax.swing.JDialog {
public static final String KEY_CARD_IMAGES_SAVE_TO_ZIP = "cardImagesSaveToZip";
public static final String KEY_CARD_IMAGES_PREF_LANGUAGE = "cardImagesPreferedImageLaguage";
public static final String KEY_CARD_RENDERING_FALLBACK = "cardRenderingFallback";
public static final String KEY_CARD_RENDERING_REMINDER_TEXT = "cardRenderingReminderText";
public static final String KEY_BACKGROUND_IMAGE = "backgroundImage";
public static final String KEY_BATTLEFIELD_IMAGE = "battlefieldImage";
public static final String KEY_BACKGROUND_IMAGE_DEFAULT = "backgroundImagedDefault";
@ -464,6 +467,9 @@ public class PreferencesDialog extends javax.swing.JDialog {
cbUseRandomBattleImage = new javax.swing.JCheckBox();
jLabel14 = new javax.swing.JLabel();
jLabel15 = new javax.swing.JLabel();
jPanel1 = new javax.swing.JPanel();
cbCardRenderImageFallback = new javax.swing.JCheckBox();
cbCardRenderShowReminderText = new javax.swing.JCheckBox();
tabSounds = new javax.swing.JPanel();
sounds_clips = new javax.swing.JPanel();
cbEnableGameSounds = new javax.swing.JCheckBox();
@ -1385,7 +1391,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
.add(jLabelEndOfTurn)
.add(checkBoxEndTurnOthers))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
.add(phases_stopSettings, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 260, Short.MAX_VALUE)
.add(phases_stopSettings, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 266, Short.MAX_VALUE)
.addContainerGap())
);
@ -1439,41 +1445,43 @@ public class PreferencesDialog extends javax.swing.JDialog {
.add(panelCardImagesLayout.createSequentialGroup()
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(panelCardImagesLayout.createSequentialGroup()
.add(24, 24, 24)
.addContainerGap()
.add(txtImageFolderPath)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(btnBrowseImageLocation))
.add(panelCardImagesLayout.createSequentialGroup()
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false)
.add(org.jdesktop.layout.GroupLayout.LEADING, panelCardImagesLayout.createSequentialGroup()
.addContainerGap()
.add(labelNumberOfDownloadThreads)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(cbNumberOfDownloadThreads, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 153, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(cbUseDefaultImageFolder)
.add(cbCheckForNewImages)
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false)
.add(panelCardImagesLayout.createSequentialGroup()
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false)
.add(org.jdesktop.layout.GroupLayout.LEADING, panelCardImagesLayout.createSequentialGroup()
.addContainerGap()
.add(labelPreferedImageLanguage))
.add(org.jdesktop.layout.GroupLayout.LEADING, cbSaveToZipFiles))
.add(40, 40, 40)
.add(cbPreferedImageLanguage, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 153, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))))
.add(0, 251, Short.MAX_VALUE)))
.add(cbCheckForNewImages)
.add(147, 147, 147))
.add(org.jdesktop.layout.GroupLayout.LEADING, panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
.add(panelCardImagesLayout.createSequentialGroup()
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false)
.add(org.jdesktop.layout.GroupLayout.LEADING, panelCardImagesLayout.createSequentialGroup()
.addContainerGap()
.add(labelPreferedImageLanguage))
.add(org.jdesktop.layout.GroupLayout.LEADING, cbSaveToZipFiles))
.add(20, 20, 20)
.add(cbPreferedImageLanguage, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 153, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.add(panelCardImagesLayout.createSequentialGroup()
.addContainerGap()
.add(labelNumberOfDownloadThreads)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(cbNumberOfDownloadThreads, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 153, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))))
.add(cbUseDefaultImageFolder))
.add(0, 231, Short.MAX_VALUE)))
.addContainerGap())
);
panelCardImagesLayout.setVerticalGroup(
panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(panelCardImagesLayout.createSequentialGroup()
.addContainerGap()
.add(cbUseDefaultImageFolder)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(txtImageFolderPath, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(btnBrowseImageLocation))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(cbCheckForNewImages)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(cbSaveToZipFiles)
@ -1484,8 +1492,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(cbPreferedImageLanguage, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(labelPreferedImageLanguage))
.addContainerGap(48, Short.MAX_VALUE))
.add(labelPreferedImageLanguage)))
);
panelBackgroundImages.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Background images setting:"));
@ -1592,6 +1599,41 @@ public class PreferencesDialog extends javax.swing.JDialog {
.add(jLabel15)))
);
jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Card rendering:"));
cbCardRenderImageFallback.setText("Fall back to plain image based rendering");
cbCardRenderImageFallback.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cbCardRenderImageFallbackActionPerformed(evt);
}
});
cbCardRenderShowReminderText.setText("Show reminder text in rendered card textboxes");
cbCardRenderShowReminderText.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cbCardRenderShowReminderTextActionPerformed(evt);
}
});
org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(jPanel1Layout.createSequentialGroup()
.add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(cbCardRenderImageFallback)
.add(cbCardRenderShowReminderText))
.add(0, 0, Short.MAX_VALUE))
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(jPanel1Layout.createSequentialGroup()
.add(cbCardRenderImageFallback)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(cbCardRenderShowReminderText)
.add(0, 7, Short.MAX_VALUE))
);
org.jdesktop.layout.GroupLayout tabImagesLayout = new org.jdesktop.layout.GroupLayout(tabImages);
tabImages.setLayout(tabImagesLayout);
tabImagesLayout.setHorizontalGroup(
@ -1600,17 +1642,22 @@ public class PreferencesDialog extends javax.swing.JDialog {
.addContainerGap()
.add(tabImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(panelCardImages, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(panelBackgroundImages, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.add(jPanel1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(tabImagesLayout.createSequentialGroup()
.add(0, 0, 0)
.add(panelBackgroundImages, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addContainerGap())
);
tabImagesLayout.setVerticalGroup(
tabImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(tabImagesLayout.createSequentialGroup()
.addContainerGap()
.add(jPanel1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(panelCardImages, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(panelBackgroundImages, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addContainerGap(90, Short.MAX_VALUE))
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
tabsPanel.addTab("Images", tabImages);
@ -2220,7 +2267,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
.add(connection_serversLayout.createSequentialGroup()
.add(141, 141, 141)
.add(jLabel17)))
.addContainerGap(111, Short.MAX_VALUE))
.addContainerGap(91, Short.MAX_VALUE))
);
connection_serversLayout.setVerticalGroup(
connection_serversLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
@ -2406,7 +2453,10 @@ public class PreferencesDialog extends javax.swing.JDialog {
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(exitButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 100, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(6, 6, 6))
.add(tabsPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.add(tabsPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
@ -2534,6 +2584,10 @@ public class PreferencesDialog extends javax.swing.JDialog {
save(prefs, dialog.cbUseDefaultBattleImage, KEY_BATTLEFIELD_IMAGE_DEFAULT, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.cbUseRandomBattleImage, KEY_BATTLEFIELD_IMAGE_RANDOM, "true", "false", UPDATE_CACHE_POLICY);
// rendering
save(prefs, dialog.cbCardRenderImageFallback, KEY_CARD_RENDERING_FALLBACK, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.cbCardRenderShowReminderText, KEY_CARD_RENDERING_REMINDER_TEXT, "true", "false", UPDATE_CACHE_POLICY);
// sounds
save(prefs, dialog.cbEnableGameSounds, KEY_SOUNDS_GAME_ON, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.cbEnableDraftSounds, KEY_SOUNDS_DRAFT_ON, "true", "false", UPDATE_CACHE_POLICY);
@ -2806,6 +2860,14 @@ public class PreferencesDialog extends javax.swing.JDialog {
// TODO add your handling code here:
}//GEN-LAST:event_cbAutoOrderTriggerActionPerformed
private void cbCardRenderImageFallbackActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbCardRenderImageFallbackActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_cbCardRenderImageFallbackActionPerformed
private void cbCardRenderShowReminderTextActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbCardRenderShowReminderTextActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_cbCardRenderShowReminderTextActionPerformed
private void cbSaveToZipFilesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbSaveToZipFilesActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_cbSaveToZipFilesActionPerformed
@ -3004,6 +3066,10 @@ public class PreferencesDialog extends javax.swing.JDialog {
dialog.cbNumberOfDownloadThreads.setSelectedItem(MageFrame.getPreferences().get(KEY_CARD_IMAGES_THREADS, "10"));
dialog.cbPreferedImageLanguage.setSelectedItem(MageFrame.getPreferences().get(KEY_CARD_IMAGES_PREF_LANGUAGE, "en"));
// rendering settings
load(prefs, dialog.cbCardRenderImageFallback, KEY_CARD_RENDERING_FALLBACK, "true");
load(prefs, dialog.cbCardRenderShowReminderText, KEY_CARD_RENDERING_REMINDER_TEXT, "true");
//add background load precedure
prop = prefs.get(KEY_BACKGROUND_IMAGE_DEFAULT, "true");
if (prop.equals("true")) {
@ -3384,6 +3450,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
private javax.swing.JCheckBox cbAllowRequestToShowHandCards;
private javax.swing.JCheckBox cbAskMoveToGraveOrder;
private javax.swing.JCheckBox cbAutoOrderTrigger;
private javax.swing.JCheckBox cbCardRenderImageFallback;
private javax.swing.JCheckBox cbCardRenderShowReminderText;
private javax.swing.JCheckBox cbCheckForNewImages;
private javax.swing.JCheckBox cbConfirmEmptyManaPool;
private javax.swing.JCheckBox cbDraftLogAutoSave;
@ -3443,6 +3511,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
private javax.swing.JLabel jLabelOpponentsTurn;
private javax.swing.JLabel jLabelUpkeep;
private javax.swing.JLabel jLabelYourTurn;
private javax.swing.JPanel jPanel1;
private javax.swing.JPanel jPanel10;
private javax.swing.JPanel jPanel11;
private javax.swing.JPanel jPanel12;

View file

@ -159,7 +159,7 @@ public abstract class Animation {
public static void transformCard(final CardPanel panel, final MagePermanent parent, final boolean transformed) {
new Animation(1200) {
new Animation(600) {
private boolean state = false;
@Override
@ -256,7 +256,7 @@ public abstract class Animation {
public void run() {
if (placeholder != null) {
placeholder.setDisplayEnabled(true);
placeholder.setImage(animationPanel);
placeholder.transferResources(animationPanel);
}
animationPanel.setVisible(false);
animationPanel.repaint();
@ -303,7 +303,7 @@ public abstract class Animation {
public void run() {
if (placeholder != null) {
placeholder.setDisplayEnabled(true);
placeholder.setImage(animationPanel);
placeholder.transferResources(animationPanel);
}
animationPanel.setVisible(false);
animationPanel.repaint();

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,711 @@
package org.mage.card.arcane;
import com.google.common.base.Function;
import com.google.common.collect.MapMaker;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.UUID;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import mage.cards.action.ActionCallback;
import mage.client.dialog.PreferencesDialog;
import mage.client.util.ImageCaches;
import mage.client.util.ImageHelper;
import mage.components.ImagePanel;
import mage.constants.AbilityType;
import mage.utils.CardUtil;
import mage.view.CardView;
import mage.view.CounterView;
import mage.view.PermanentView;
import mage.view.StackAbilityView;
import net.java.truevfs.access.TFile;
import org.apache.log4j.Logger;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import static org.mage.plugins.card.constants.Constants.THUMBNAIL_SIZE_FULL;
import org.mage.plugins.card.dl.sources.DirectLinksForDownload;
import org.mage.plugins.card.images.ImageCache;
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
/**
* Class for drawing the mage card object by using a form based JComponent approach
*
* @author arcane, nantuko, noxx, stravant
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class CardPanelComponentImpl extends CardPanel {
private static final long serialVersionUID = -3272134219262184411L;
private static final Logger LOGGER = Logger.getLogger(CardPanelComponentImpl.class);
private static final int WIDTH_LIMIT = 90; // card width limit to create smaller counter
private static final float ROUNDED_CORNER_SIZE = 0.1f;
private static final float BLACK_BORDER_SIZE = 0.03f;
private static final int TEXT_GLOW_SIZE = 6;
private static final float TEXT_GLOW_INTENSITY = 3f;
public final ScaledImagePanel imagePanel;
public ImagePanel overlayPanel;
public JPanel iconPanel;
private JButton typeButton;
public JPanel counterPanel;
private JLabel loyaltyCounterLabel;
private JLabel plusCounterLabel;
private JLabel otherCounterLabel;
private JLabel minusCounterLabel;
private int loyaltyCounter;
private int plusCounter;
private int otherCounter;
private int minusCounter;
private int lastCardWidth;
private final GlowText titleText;
private final GlowText ptText;
private boolean hasImage = false;
private boolean displayTitleAnyway;
private final static Map<Key, BufferedImage> IMAGE_CACHE;
class Key {
final int width;
final int height;
final int cardWidth;
final int cardHeight;
final int cardXOffset;
final int cardYOffset;
final boolean hasImage;
final boolean isSelected;
final boolean isChoosable;
final boolean isPlayable;
final boolean canAttack;
public Key(int width, int height, int cardWidth, int cardHeight, int cardXOffset, int cardYOffset, boolean hasImage, boolean isSelected, boolean isChoosable, boolean isPlayable, boolean canAttack) {
this.width = width;
this.height = height;
this.cardWidth = cardWidth;
this.cardHeight = cardHeight;
this.cardXOffset = cardXOffset;
this.cardYOffset = cardYOffset;
this.hasImage = hasImage;
this.isSelected = isSelected;
this.isChoosable = isChoosable;
this.isPlayable = isPlayable;
this.canAttack = canAttack;
}
@Override
public int hashCode() {
int hash = 3;
hash = 19 * hash + this.width;
hash = 19 * hash + this.height;
hash = 19 * hash + this.cardWidth;
hash = 19 * hash + this.cardHeight;
hash = 19 * hash + this.cardXOffset;
hash = 19 * hash + this.cardYOffset;
hash = 19 * hash + (this.hasImage ? 1 : 0);
hash = 19 * hash + (this.isSelected ? 1 : 0);
hash = 19 * hash + (this.isChoosable ? 1 : 0);
hash = 19 * hash + (this.isPlayable ? 1 : 0);
hash = 19 * hash + (this.canAttack ? 1 : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Key other = (Key) obj;
if (this.width != other.width) {
return false;
}
if (this.height != other.height) {
return false;
}
if (this.cardWidth != other.cardWidth) {
return false;
}
if (this.cardHeight != other.cardHeight) {
return false;
}
if (this.cardXOffset != other.cardXOffset) {
return false;
}
if (this.cardYOffset != other.cardYOffset) {
return false;
}
if (this.hasImage != other.hasImage) {
return false;
}
if (this.isSelected != other.isSelected) {
return false;
}
if (this.isChoosable != other.isChoosable) {
return false;
}
if (this.isPlayable != other.isPlayable) {
return false;
}
if (this.canAttack != other.canAttack) {
return false;
}
return true;
}
}
static {
IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap(new Function<Key, BufferedImage>() {
@Override
public BufferedImage apply(Key key) {
return createImage(key);
}
}));
}
public CardPanelComponentImpl(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension) {
// Call to super
super(newGameCard, gameId, loadImage, callback, foil, dimension);
// Counter panel
if (!newGameCard.isAbility()) {
// panel to show counters on the card
counterPanel = new JPanel();
counterPanel.setLayout(null);
counterPanel.setOpaque(false);
add(counterPanel);
plusCounterLabel = new JLabel("");
plusCounterLabel.setToolTipText("+1/+1");
counterPanel.add(plusCounterLabel);
minusCounterLabel = new JLabel("");
minusCounterLabel.setToolTipText("-1/-1");
counterPanel.add(minusCounterLabel);
loyaltyCounterLabel = new JLabel("");
loyaltyCounterLabel.setToolTipText("loyalty");
counterPanel.add(loyaltyCounterLabel);
otherCounterLabel = new JLabel("");
counterPanel.add(otherCounterLabel);
counterPanel.setVisible(false);
}
// Ability icon
if (newGameCard.isAbility()) {
if (AbilityType.TRIGGERED.equals(newGameCard.getAbilityType())) {
setTypeIcon(ImageManagerImpl.getInstance().getTriggeredAbilityImage(), "Triggered Ability");
} else if (AbilityType.ACTIVATED.equals(newGameCard.getAbilityType())) {
setTypeIcon(ImageManagerImpl.getInstance().getActivatedAbilityImage(), "Activated Ability");
}
}
// Token icon
if (this.gameCard.isToken()) {
setTypeIcon(ImageManagerImpl.getInstance().getTokenIconImage(), "Token Permanent");
}
displayTitleAnyway = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_CARD_NAMES, "true").equals("true");
// Title Text
titleText = new GlowText();
setText(gameCard);
// int fontSize = (int) cardHeight / 11;
// titleText.setFont(getFont().deriveFont(Font.BOLD, fontSize));
titleText.setForeground(Color.white);
titleText.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY);
titleText.setWrap(true);
add(titleText);
// PT Text
ptText = new GlowText();
if (CardUtil.isCreature(gameCard)) {
ptText.setText(gameCard.getPower() + "/" + gameCard.getToughness());
} else if (CardUtil.isPlaneswalker(gameCard)) {
ptText.setText(gameCard.getLoyalty());
}
// ptText.setFont(getFont().deriveFont(Font.BOLD, fontSize));
ptText.setForeground(Color.white);
ptText.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY);
add(ptText);
// Sickness overlay
BufferedImage sickness = ImageManagerImpl.getInstance().getSicknessImage();
overlayPanel = new ImagePanel(sickness, ImagePanel.SCALED);
overlayPanel.setOpaque(false);
add(overlayPanel);
// Imagel panel
imagePanel = new ScaledImagePanel();
imagePanel.setBorder(BorderFactory.createLineBorder(Color.white));
add(imagePanel);
// Do we need to load?
if (loadImage) {
initialDraw();
} else {
// Nothing to do
}
}
private void setTypeIcon(BufferedImage bufferedImage, String toolTipText) {
iconPanel = new JPanel();
iconPanel.setLayout(null);
iconPanel.setOpaque(false);
add(iconPanel);
typeButton = new JButton("");
typeButton.setLocation(2, 2);
typeButton.setSize(25, 25);
iconPanel.setVisible(true);
typeButton.setIcon(new ImageIcon(bufferedImage));
if (toolTipText != null) {
typeButton.setToolTipText(toolTipText);
}
iconPanel.add(typeButton);
}
@Override
public void cleanUp() {
super.cleanUp();
this.counterPanel = null;
}
private void setText(CardView card) {
titleText.setText(!displayTitleAnyway && hasImage ? "" : card.getName());
}
private void setImage(BufferedImage srcImage) {
synchronized (imagePanel) {
if (srcImage != null) {
imagePanel.setImage(srcImage);
} else {
imagePanel.clearImage();
}
repaint();
}
doLayout();
}
@Override
public void transferResources(final CardPanel panelAbstract) {
if (panelAbstract instanceof CardPanelComponentImpl) {
CardPanelComponentImpl panel = (CardPanelComponentImpl)panelAbstract;
synchronized (panel.imagePanel) {
if (panel.imagePanel.hasImage()) {
setImage(panel.imagePanel.getSrcImage());
}
}
}
}
@Override
public void setSelected(boolean isSelected) {
super.setSelected(isSelected);
if (isSelected) {
this.titleText.setGlowColor(Color.green);
} else {
this.titleText.setGlowColor(Color.black);
}
}
@Override
protected void paintCard(Graphics2D g2d) {
float alpha = getAlpha();
if (alpha != 1.0f) {
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha);
g2d.setComposite(composite);
}
g2d.drawImage(
IMAGE_CACHE.get(
new Key(getWidth(), getHeight(), getCardWidth(), getCardHeight(), getCardXOffset(), getCardYOffset(),
hasImage, isSelected(), isChoosable(), gameCard.isPlayable(), gameCard.isCanAttack())),
0, 0, null);
g2d.dispose();
}
private static BufferedImage createImage(Key key) {
int cardWidth = key.cardWidth;
int cardHeight = key.cardHeight;
int cardXOffset = key.cardXOffset;
int cardYOffset = key.cardYOffset;
BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(key.width, key.height);
Graphics2D g2d = image.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (!key.hasImage) {
g2d.setColor(new Color(30, 200, 200, 120));
} else {
g2d.setColor(new Color(0, 0, 0, 255));
}
int cornerSize = Math.max(4, Math.round(cardWidth * ROUNDED_CORNER_SIZE));
g2d.fillRoundRect(cardXOffset, cardYOffset, cardWidth, cardHeight, cornerSize, cornerSize);
if (key.isSelected) {
g2d.setColor(Color.green);
g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize);
} else if (key.isChoosable) {
g2d.setColor(new Color(250, 250, 0, 230));
g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize);
} else if (key.isPlayable) {
g2d.setColor(new Color(153, 102, 204, 200));
//g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize);
g2d.fillRoundRect(cardXOffset, cardYOffset, cardWidth, cardHeight, cornerSize, cornerSize);
}
if (key.canAttack) {
g2d.setColor(new Color(0, 0, 255, 230));
g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize);
}
//TODO:uncomment
/*
if (gameCard.isAttacking()) {
g2d.setColor(new Color(200,10,10,200));
g2d.fillRoundRect(cardXOffset+1, cardYOffset+1, cardWidth-2, cardHeight-2, cornerSize, cornerSize);
}*/
g2d.dispose();
return image;
}
@Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
if (getShowCastingCost() && !isAnimationPanel() && getCardWidth() < 200 && getCardWidth() > 60) {
String manaCost = ManaSymbols.getStringManaCost(gameCard.getManaCost());
int width = getWidth(manaCost);
if (hasImage) {
ManaSymbols.draw(g, manaCost, getCardXOffset() + getCardWidth() - width - 5, getCardYOffset() + 5, getSymbolWidth());
} else {
ManaSymbols.draw(g, manaCost, getCardXOffset() + 8, getCardHeight() - 9, getSymbolWidth());
}
}
}
private int getWidth(String manaCost) {
int width = 0;
manaCost = manaCost.replace("\\", "");
StringTokenizer tok = new StringTokenizer(manaCost, " ");
while (tok.hasMoreTokens()) {
tok.nextToken();
width += getSymbolWidth();
}
return width;
}
@Override
public void doLayout() {
super.doLayout();
int cardWidth = getCardWidth();
int cardHeight = getCardHeight();
int cardXOffset = getCardXOffset();
int cardYOffset = getCardYOffset();
int borderSize = Math.round(cardWidth * BLACK_BORDER_SIZE);
imagePanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize);
imagePanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2);
if (hasSickness() && CardUtil.isCreature(gameCard) && isPermanent()) {
overlayPanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize);
overlayPanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2);
} else {
overlayPanel.setVisible(false);
}
if (iconPanel != null) {
iconPanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize);
iconPanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2);
}
if (counterPanel != null) {
counterPanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize);
counterPanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2);
int size = cardWidth > WIDTH_LIMIT ? 40 : 20;
minusCounterLabel.setLocation(counterPanel.getWidth() - size, counterPanel.getHeight() - size * 2);
minusCounterLabel.setSize(size, size);
plusCounterLabel.setLocation(5, counterPanel.getHeight() - size * 2);
plusCounterLabel.setSize(size, size);
loyaltyCounterLabel.setLocation(counterPanel.getWidth() - size, counterPanel.getHeight() - size);
loyaltyCounterLabel.setSize(size, size);
otherCounterLabel.setLocation(5, counterPanel.getHeight() - size);
otherCounterLabel.setSize(size, size);
}
int fontHeight = Math.round(cardHeight * (27f / 680));
boolean showText = (!isAnimationPanel() && fontHeight < 12);
titleText.setVisible(showText);
ptText.setVisible(showText);
if (showText) {
int fontSize = (int) cardHeight / 11;
titleText.setFont(getFont().deriveFont(Font.BOLD, fontSize));
int titleX = Math.round(cardWidth * (20f / 480));
int titleY = Math.round(cardHeight * (9f / 680)) + getTextOffset();
titleText.setBounds(cardXOffset + titleX, cardYOffset + titleY, cardWidth - titleX, cardHeight - titleY);
ptText.setFont(getFont().deriveFont(Font.BOLD, fontSize));
Dimension ptSize = ptText.getPreferredSize();
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);
ptText.setLocation(cardXOffset + ptX - TEXT_GLOW_SIZE / 2 - offsetX, cardYOffset + ptY - TEXT_GLOW_SIZE / 2);
}
}
@Override
public String toString() {
return gameCard.toString();
}
@Override
public void setCardBounds(int x, int y, int cardWidth, int cardHeight) {
// Call to super
super.setCardBounds(x, y, cardWidth, cardHeight);
// Update image
if (imagePanel != null && imagePanel.getSrcImage() != null) {
updateImage();
}
}
@Override
public void setAlpha(float alpha) {
super.setAlpha(alpha);
// Update components
if (alpha == 0) {
this.ptText.setVisible(false);
this.titleText.setVisible(false);
} else if (alpha == 1.0f) {
this.ptText.setVisible(true);
this.titleText.setVisible(true);
}
}
///////////////////////////////////////////////////////////
// Image updating code
private int updateImageStamp;
@Override
public void updateImage() {
tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0;
flippedAngle = isFlipped() ? CardPanel.FLIPPED_ANGLE : 0;
//final CardView gameCard = this.gameCard;
final int stamp = ++updateImageStamp;
Util.threadPool.submit(new Runnable() {
@Override
public void run() {
try {
final BufferedImage srcImage;
if (gameCard.isFaceDown()) {
srcImage = getFaceDownImage();
} else if (getCardWidth() > THUMBNAIL_SIZE_FULL.width) {
srcImage = ImageCache.getImage(gameCard, getCardWidth(), getCardHeight());
} else {
srcImage = ImageCache.getThumbnail(gameCard);
}
UI.invokeLater(new Runnable() {
@Override
public void run() {
if (stamp == updateImageStamp) {
hasImage = srcImage != null;
setText(gameCard);
setImage(srcImage);
}
}
});
} catch (Exception e) {
e.printStackTrace();
} catch (Error err) {
err.printStackTrace();
}
}
});
}
private BufferedImage getFaceDownImage() {
if (isPermanent()) {
if (((PermanentView) gameCard).isMorphed()) {
return ImageCache.getMorphImage();
} else {
return ImageCache.getManifestImage();
}
} else if (this.gameCard instanceof StackAbilityView) {
return ImageCache.getMorphImage();
} else {
return ImageCache.loadImage(new TFile(DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename));
}
}
@Override
public void showCardTitle() {
displayTitleAnyway = true;
setText(gameCard);
}
@Override
public void update(CardView card) {
// Super
super.update(card);
// Update card text
if (CardUtil.isCreature(card) && CardUtil.isPlaneswalker(card)) {
ptText.setText(card.getPower() + "/" + card.getToughness() + " (" + card.getLoyalty() + ")");
} else if (CardUtil.isCreature(card)) {
ptText.setText(card.getPower() + "/" + card.getToughness());
} else if (CardUtil.isPlaneswalker(card)) {
ptText.setText(card.getLoyalty());
} else {
ptText.setText("");
}
setText(card);
// Summoning Sickness overlay
if (hasSickness() && CardUtil.isCreature(gameCard) && isPermanent()) {
overlayPanel.setVisible(true);
} else {
overlayPanel.setVisible(false);
}
// Update counters panel
if (counterPanel != null) {
updateCounters(card);
}
// Finally, queue a repaint
repaint();
}
private void updateCounters(CardView card) {
if (card.getCounters() != null && !card.getCounters().isEmpty()) {
String name = "";
if (lastCardWidth != getCardWidth()) {
lastCardWidth = getCardWidth();
plusCounter = 0;
minusCounter = 0;
otherCounter = 0;
loyaltyCounter = 0;
}
plusCounterLabel.setVisible(false);
minusCounterLabel.setVisible(false);
loyaltyCounterLabel.setVisible(false);
otherCounterLabel.setVisible(false);
for (CounterView counterView : card.getCounters()) {
if (counterView.getCount() == 0) {
continue;
}
switch (counterView.getName()) {
case "+1/+1":
if (counterView.getCount() != plusCounter) {
plusCounter = counterView.getCount();
plusCounterLabel.setIcon(getCounterImageWithAmount(plusCounter, ImageManagerImpl.getInstance().getCounterImageGreen(), getCardWidth()));
}
plusCounterLabel.setVisible(true);
break;
case "-1/-1":
if (counterView.getCount() != minusCounter) {
minusCounter = counterView.getCount();
minusCounterLabel.setIcon(getCounterImageWithAmount(minusCounter, ImageManagerImpl.getInstance().getCounterImageRed(), getCardWidth()));
}
minusCounterLabel.setVisible(true);
break;
case "loyalty":
if (counterView.getCount() != loyaltyCounter) {
loyaltyCounter = counterView.getCount();
loyaltyCounterLabel.setIcon(getCounterImageWithAmount(loyaltyCounter, ImageManagerImpl.getInstance().getCounterImageViolet(), getCardWidth()));
}
loyaltyCounterLabel.setVisible(true);
break;
default:
if (name.isEmpty()) { // only first other counter is shown
name = counterView.getName();
otherCounter = counterView.getCount();
otherCounterLabel.setToolTipText(name);
otherCounterLabel.setIcon(getCounterImageWithAmount(otherCounter, ImageManagerImpl.getInstance().getCounterImageGrey(), getCardWidth()));
otherCounterLabel.setVisible(true);
}
}
}
counterPanel.setVisible(true);
} else {
plusCounterLabel.setVisible(false);
minusCounterLabel.setVisible(false);
loyaltyCounterLabel.setVisible(false);
otherCounterLabel.setVisible(false);
counterPanel.setVisible(false);
}
}
private static ImageIcon getCounterImageWithAmount(int amount, BufferedImage image, int cardWidth) {
int factor = cardWidth > WIDTH_LIMIT ? 2 : 1;
int xOffset = amount > 9 ? 2 : 5;
int fontSize = factor == 1 ? amount < 10 ? 12 : amount < 100 ? 10 : amount < 1000 ? 7 : 6
: amount < 10 ? 19 : amount < 100 ? 15 : amount < 1000 ? 12 : amount < 10000 ? 9 : 8;
BufferedImage newImage;
if (cardWidth > WIDTH_LIMIT) {
newImage = ImageManagerImpl.deepCopy(image);
} else {
newImage = ImageHelper.getResizedImage(image, 20, 20);
}
Graphics graphics = newImage.getGraphics();
graphics.setColor(Color.BLACK);
graphics.setFont(new Font("Arial Black", amount > 100 ? Font.PLAIN : Font.BOLD, fontSize));
graphics.drawString(Integer.toString(amount), xOffset * factor, 11 * factor);
return new ImageIcon(newImage);
}
@Override
public Image getImage() {
if (this.hasImage) {
if (gameCard.isFaceDown()) {
return getFaceDownImage();
} else {
return ImageCache.getImageOriginal(gameCard);
}
}
return null;
}
}

View file

@ -0,0 +1,323 @@
package org.mage.card.arcane;
import com.google.common.base.Function;
import com.google.common.collect.MapMaker;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.Map;
import java.util.UUID;
import mage.cards.action.ActionCallback;
import mage.client.util.ImageCaches;
import mage.constants.CardType;
import mage.view.CardView;
import org.apache.log4j.Logger;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import static org.mage.plugins.card.constants.Constants.THUMBNAIL_SIZE_FULL;
import org.mage.plugins.card.images.ImageCache;
public class CardPanelRenderImpl extends CardPanel {
private static final Logger LOGGER = Logger.getLogger(CardPanelRenderImpl.class);
private static boolean cardViewEquals(CardView a, CardView b) {
if (a == b) {
return true;
}
if (!a.getName().equals(b.getName())) {
return false;
}
if (!a.getPower().equals(b.getPower())) {
return false;
}
if (!a.getToughness().equals(b.getToughness())) {
return false;
}
if (!a.getLoyalty().equals(b.getLoyalty())) {
return false;
}
if (0 != a.getColor().compareTo(b.getColor())) {
return false;
}
if (!a.getCardTypes().equals(b.getCardTypes())) {
return false;
}
if (!a.getSubTypes().equals(b.getSubTypes())) {
return false;
}
if (!a.getSuperTypes().equals(b.getSuperTypes())) {
return false;
}
if (!a.getManaCost().equals(b.getManaCost())) {
return false;
}
if (!a.getRules().equals(b.getRules())) {
return false;
}
if (!a.getExpansionSetCode().equals(b.getExpansionSetCode())) {
return false;
}
return true;
}
class ImageKey {
final BufferedImage artImage;
final int width;
final int height;
final boolean isChoosable;
final boolean isSelected;
final CardView view;
final int hashCode;
public ImageKey(CardView view, BufferedImage artImage, int width, int height, boolean isChoosable, boolean isSelected) {
this.view = view;
this.artImage = artImage;
this.width = width;
this.height = height;
this.isChoosable = isChoosable;
this.isSelected = isSelected;
this.hashCode = hashCodeImpl();
}
private int hashCodeImpl() {
StringBuilder sb = new StringBuilder();
sb.append((char)(artImage != null ? 1 : 0));
sb.append((char)width);
sb.append((char)height);
sb.append((char)(isSelected ? 1 : 0));
sb.append((char)(isChoosable ? 1 : 0));
sb.append((char)(this.view.isPlayable() ? 1 : 0));
sb.append((char)(this.view.isCanAttack() ? 1 : 0));
sb.append(this.view.getName());
sb.append(this.view.getPower());
sb.append(this.view.getToughness());
sb.append(this.view.getLoyalty());
sb.append(this.view.getColor().toString());
sb.append(this.view.getExpansionSetCode());
for (CardType type: this.view.getCardTypes()) {
sb.append((char)type.ordinal());
}
for (String s: this.view.getSuperTypes()) {
sb.append(s);
}
for (String s: this.view.getSubTypes()) {
sb.append(s);
}
for (String s: this.view.getManaCost()) {
sb.append(s);
}
for (String s: this.view.getRules()) {
sb.append(s);
}
return sb.toString().hashCode();
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object object) {
// Initial checks
if (this == object) {
return true;
}
if (object == null) {
return false;
}
if (!(object instanceof ImageKey)) {
return false;
}
final ImageKey other = (ImageKey)object;
// Compare
if ((artImage != null) != (other.artImage != null)) {
return false;
}
if (width != other.width) {
return false;
}
if (height != other.height) {
return false;
}
if (isChoosable != other.isChoosable) {
return false;
}
if (isSelected != other.isSelected) {
return false;
}
return cardViewEquals(view, other.view);
}
}
// Map of generated images
private final static Map<ImageKey, BufferedImage> IMAGE_CACHE = new MapMaker().softValues().makeMap();
// The art image for the card, loaded in from the disk
private BufferedImage artImage;
// The rendered card image, with or without the art image loaded yet
// = null while invalid
private BufferedImage cardImage;
private CardRenderer cardRenderer;
public CardPanelRenderImpl(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension) {
// Call to super
super(newGameCard, gameId, loadImage, callback, foil, dimension);
// Renderer
cardRenderer = new ModernCardRenderer(gameCard, isTransformed());
// Draw the parts
initialDraw();
}
@Override
public void transferResources(CardPanel panel) {
if (panel instanceof CardPanelRenderImpl) {
CardPanelRenderImpl impl = (CardPanelRenderImpl)panel;
// Use the art image and current rendered image from the card
artImage = impl.artImage;
cardRenderer.setArtImage(artImage);
cardImage = impl.cardImage;
}
}
@Override
protected void paintCard(Graphics2D g) {
// Render the card if we don't have an image ready to use
if (cardImage == null) {
// Try to get card image from cache based on our card characteristics
ImageKey key =
new ImageKey(gameCard, artImage,
getCardWidth(), getCardHeight(),
isChoosable(), isSelected());
cardImage = IMAGE_CACHE.get(key);
// No cached copy exists? Render one and cache it
if (cardImage == null) {
cardImage = renderCard();
IMAGE_CACHE.put(key, cardImage);
}
}
// And draw the image we now have
g.drawImage(cardImage, 0, 0, null);
}
/**
* Render the card to a new BufferedImage at it's current dimensions
* @return
*/
private BufferedImage renderCard() {
int cardWidth = getCardWidth();
int cardHeight = getCardHeight();
int cardXOffset = getCardXOffset();
int cardYOffset = getCardYOffset();
// Create image to render to
BufferedImage image =
GraphicsUtilities.createCompatibleTranslucentImage(getWidth(), getHeight());
Graphics2D g2d = image.createGraphics();
// Render with Antialialsing
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Draw card itself
g2d.translate(cardXOffset, cardYOffset);
cardRenderer.draw(g2d, cardWidth - cardXOffset, cardHeight - cardYOffset);
g2d.translate(-cardXOffset, -cardYOffset);
// Done
g2d.dispose();
return image;
}
private int updateImageStamp;
@Override
public void updateImage() {
// Invalidate
artImage = null;
cardImage = null;
// Stop animation
tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0;
flippedAngle = isFlipped() ? CardPanel.FLIPPED_ANGLE : 0;
// Schedule a repaint
repaint();
// Submit a task to draw with the card art when it arrives
final int stamp = ++updateImageStamp;
Util.threadPool.submit(new Runnable() {
@Override
public void run() {
try {
final BufferedImage srcImage;
if (gameCard.isFaceDown()) {
// Nothing to do
srcImage = null;
} else if (getCardWidth() > THUMBNAIL_SIZE_FULL.width) {
srcImage = ImageCache.getImage(gameCard, getCardWidth(), getCardHeight());
} else {
srcImage = ImageCache.getThumbnail(gameCard);
}
UI.invokeLater(new Runnable() {
@Override
public void run() {
if (stamp == updateImageStamp) {
CardPanelRenderImpl.this.artImage = srcImage;
CardPanelRenderImpl.this.cardRenderer.setArtImage(srcImage);
if (srcImage != null) {
// Invalidate and repaint
CardPanelRenderImpl.this.cardImage = null;
repaint();
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
} catch (Error err) {
err.printStackTrace();
}
}
});
}
@Override
public void update(CardView card) {
// Update super
super.update(card);
// Update renderer
cardRenderer = new ModernCardRenderer(gameCard, isTransformed());
// Repaint
repaint();
}
@Override
public void setCardBounds(int x, int y, int cardWidth, int cardHeight) {
super.setCardBounds(x, y, cardWidth, cardHeight);
// Rerender
cardImage = null;
}
@Override
public Image getImage() {
// Render impl never returns a card image
return artImage;
}
@Override
public void showCardTitle() {
// Nothing to do, rendered cards always have a title
}
}

View file

@ -0,0 +1,348 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.card.arcane;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.List;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.counters.Counter;
import mage.view.CardView;
import mage.view.CounterView;
import mage.view.PermanentView;
import org.apache.log4j.Logger;
/**
* @author stravant@gmail.com
*
* Common base class for card renderers for each card frame / card type.
*
* Follows the template method pattern to implement a new renderer, implement
* the following methods (they are called in the following order):
*
* * drawBorder()
* Draws the outermost border of the card, white border or black border
*
* * drawBackground()
* Draws the background texture / color of the card
*
* * drawArt()
* Draws the card's art
*
* * drawFrame()
* Draws the card frame (over the art and background)
*
* * drawOverlays()
* Draws summoning sickness and possible other overlays
*
* * drawCounters()
* Draws counters on the card, such as +1/+1 and -1/-1 counters
*
* Predefined methods that the implementations can use:
*
* * drawRules(font, bounding box)
*
* * drawNameLine(font, bounding box)
*
* * drawTypeLine(font, bounding box)
*
*/
public abstract class CardRenderer {
private static final Logger LOGGER = Logger.getLogger(CardPanel.class);
///////////////////////////////////////////////////////////////////////////
// Common layout metrics between all cards
// The card to be rendered
protected final CardView cardView;
// Is the card transformed?
protected final boolean isTransformed;
// The card image
protected BufferedImage artImage;
///////////////////////////////////////////////////////////////////////////
// Common layout metrics between all cards
// Polygons for counters
private static final Polygon PLUS_COUNTER_POLY = new Polygon(new int[]{
0, 5, 10, 10, 5, 0
}, new int[]{
3, 0, 3, 10, 9, 10
}, 6);
private static final Polygon MINUS_COUNTER_POLY = new Polygon(new int[]{
0, 5, 10, 10, 5, 0
}, new int[]{
0, 1, 0, 7, 10, 7
}, 6);
private static final Polygon TIME_COUNTER_POLY = new Polygon(new int[]{
0, 10, 8, 10, 0, 2
}, new int[]{
0, 0, 5, 10, 10, 5
}, 6);
private static final Polygon OTHER_COUNTER_POLY = new Polygon(new int[]{
1, 9, 9, 1
}, new int[]{
1, 1, 9, 9
}, 4);
// Paint for a card back
public static Paint BG_TEXTURE_CARDBACK = new Color(153, 102, 51);
// The size of the card
protected int cardWidth;
protected int cardHeight;
// Radius of the corners of the cards
protected static float CORNER_RADIUS_FRAC = 0.1f; //x cardWidth
protected static int CORNER_RADIUS_MIN = 3;
protected int cornerRadius;
// The inset of the actual card from the black / white border around it
protected static float BORDER_WIDTH_FRAC = 0.03f; //x cardWidth
protected static float BORDER_WIDTH_MIN = 2;
protected int borderWidth;
// The parsed text of the card
protected ArrayList<TextboxRule> textboxRules = new ArrayList<>();
protected ArrayList<TextboxRule> textboxKeywords = new ArrayList<>();
// The Construtor
// The constructor should prepare all of the things that it can
// without knowing the dimensions that the card will be rendered at.
// Then, the CardRenderer can be called on multiple times to render the
// card at various sizes (for instance, during animation)
public CardRenderer(CardView card, boolean isTransformed) {
// Set base parameters
this.cardView = card;
this.isTransformed = isTransformed;
// Translate the textbox text
for (String rule: card.getRules()) {
TextboxRule tbRule = TextboxRuleParser.parse(card, rule);
if (tbRule.type == TextboxRuleType.SIMPLE_KEYWORD) {
textboxKeywords.add(tbRule);
} else {
textboxRules.add(tbRule);
}
}
}
// Layout operation
// Calculate common layout metrics that will be used by several
// of the operations in the template method.
protected void layout(int cardWidth, int cardHeight) {
// Store the dimensions for the template methods to use
this.cardWidth = cardWidth;
this.cardHeight = cardHeight;
// Corner radius and border width
cornerRadius = (int)Math.max(
CORNER_RADIUS_MIN,
CORNER_RADIUS_FRAC * cardWidth);
borderWidth = (int)Math.max(
BORDER_WIDTH_MIN,
BORDER_WIDTH_FRAC * cardWidth);
}
// The Draw Method
// The draw method takes the information caculated by the constructor
// and uses it to draw to a concrete size of card and graphics.
public void draw(Graphics2D g, int cardWidth, int cardHeight) {
// Pre template method layout, to calculate shared layout info
layout(cardWidth, cardHeight);
// Call the template methods
drawBorder(g);
drawBackground(g);
drawArt(g);
drawFrame(g);
drawOverlays(g);
drawCounters(g);
}
// Template methods to be implemented by sub classes
// For instance, for the Modern vs Old border card frames
protected abstract void drawBorder(Graphics2D g);
protected abstract void drawBackground(Graphics2D g);
protected abstract void drawArt(Graphics2D g);
protected abstract void drawFrame(Graphics2D g);
// Template methods that are possible to override, but unlikely to be
// overridden.
// Draw the card back
protected void drawCardBack(Graphics2D g) {
g.setPaint(BG_TEXTURE_CARDBACK);
g.fillRect(borderWidth, borderWidth,
cardWidth - 2*borderWidth, cardHeight - 2*borderWidth);
}
// Draw summoning sickness overlay, and possibly other overlays
protected void drawOverlays(Graphics2D g) {
if (cardView instanceof PermanentView) {
if (((PermanentView)cardView).hasSummoningSickness()) {
int x1 = (int)(0.2*cardWidth);
int x2 = (int)(0.8*cardWidth);
int y1 = (int)(0.2*cardHeight);
int y2 = (int)(0.8*cardHeight);
int xPoints[] = {
x1, x2, x1, x2
};
int yPoints[] = {
y1, y1, y2, y2
};
g.setColor(new Color(255, 255, 255, 200));
g.setStroke(new BasicStroke(7));
g.drawPolygon(xPoints, yPoints, 4);
g.setColor(new Color(0, 0, 0, 200));
g.setStroke(new BasicStroke(5));
g.drawPolygon(xPoints, yPoints, 4);
g.setStroke(new BasicStroke(1));
int[] xPoints2 = {
x1, x2, cardWidth/2
};
int[] yPoints2 = {
y1, y1, cardHeight/2
};
g.setColor(new Color(0, 0, 0, 100));
g.fillPolygon(xPoints2, yPoints2, 3);
}
}
}
// Draw +1/+1 and other counters
protected void drawCounters(Graphics2D g) {
int xPos = (int)(0.7*cardWidth);
int yPos = (int)(0.15*cardHeight);
if (cardView.getCounters() != null) {
for (CounterView v: cardView.getCounters()) {
// Don't render loyalty, we do that in the bottom corner
if (!v.getName().equals("loyalty")) {
Polygon p;
if (v.getName().equals("+1/+1")) {
p = PLUS_COUNTER_POLY;
} else if (v.getName().equals("-1/-1")) {
p = MINUS_COUNTER_POLY;
} else if (v.getName().equals("time")) {
p = TIME_COUNTER_POLY;
} else {
p = OTHER_COUNTER_POLY;
}
double scale = (0.1*0.18*cardWidth);
Graphics2D g2 = (Graphics2D)g.create();
g2.translate(xPos, yPos);
g2.scale(scale, scale);
g2.setColor(Color.white);
g2.fillPolygon(p);
g2.setColor(Color.black);
g2.drawPolygon(p);
g2.setFont(new Font("Arial", Font.BOLD, 7));
String cstr = "" + v.getCount();
int strW = g2.getFontMetrics().stringWidth(cstr);
g2.drawString(cstr, 5 - strW/2, 8);
g2.dispose();
yPos += ((int)(0.22*cardWidth));
}
}
}
}
// Draw an expansion symbol, right justified, in a given region
// Return the width of the drawn symbol
protected int drawExpansionSymbol(Graphics2D g, int x, int y, int w, int h) {
// Draw the expansion symbol
Image setSymbol = ManaSymbols.getSetSymbolImage(cardView.getExpansionSetCode(), cardView.getRarity().getCode());
int setSymbolWidth;
if (setSymbol == null) {
// Just draw the as a code
String code = cardView.getExpansionSetCode().toUpperCase();
FontMetrics metrics = g.getFontMetrics();
setSymbolWidth = metrics.stringWidth(code);
if (cardView.getRarity() == Rarity.COMMON) {
g.setColor(Color.white);
} else {
g.setColor(Color.black);
}
g.fillRoundRect(
x + w - setSymbolWidth - 1, y + 2,
setSymbolWidth+2, h - 5,
5, 5);
g.setColor(getRarityColor());
g.drawString(code, x + w - setSymbolWidth, y + h - 3);
} else {
// Draw the set symbol
int height = setSymbol.getHeight(null);
int scale = 1;
if (height != -1) {
while (height > h+2) {
scale *= 2;
height /= 2;
}
}
setSymbolWidth = setSymbol.getWidth(null) / scale;
g.drawImage(setSymbol,
x + w - setSymbolWidth, y + (h - height)/2,
setSymbolWidth, height,
null);
}
return setSymbolWidth;
}
private Color getRarityColor() {
switch (cardView.getRarity()) {
case RARE:
return new Color(255, 191, 0);
case UNCOMMON:
return new Color(192, 192, 192);
case MYTHIC:
return new Color(213, 51, 11);
case SPECIAL:
return new Color(204, 0, 255);
case BONUS:
return new Color(129, 228, 228);
case COMMON:
default:
return Color.black;
}
}
// Get a string representing the type line
protected String getCardTypeLine() {
StringBuilder sbType = new StringBuilder();
for (String superType : cardView.getSuperTypes()) {
sbType.append(superType).append(" ");
}
for (CardType cardType : cardView.getCardTypes()) {
sbType.append(cardType.toString()).append(" ");
}
if (cardView.getSubTypes().size() > 0) {
sbType.append("- ");
for (String subType : cardView.getSubTypes()) {
sbType.append(subType).append(" ");
}
}
return sbType.toString();
}
// Set the card art image (CardPanel will give it to us when it
// is loaded and ready)
public void setArtImage(Image image) {
artImage = CardRendererUtils.toBufferedImage(image);
}
}

View file

@ -0,0 +1,124 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.card.arcane;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author stravant@gmail.com
*
* Various static utilities for use in the card renderer
*/
public class CardRendererUtils {
/**
* Convert an abstract image, whose underlying implementation may or may
* not be a BufferedImage into a BufferedImage by creating one and coping
* the contents if it is not, and simply up-casting if it is.
* @param img The image to convert
* @return The converted image
*/
public static BufferedImage toBufferedImage(Image img) {
// Null? No conversion to do
if (img == null) {
return null;
}
// Already a buffered image?
if (img instanceof BufferedImage) {
return (BufferedImage) img;
}
// Create a buffered image with transparency
BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
// Draw the image on to the buffered image
Graphics2D bGr = bimage.createGraphics();
bGr.drawImage(img, 0, 0, null);
bGr.dispose();
// Return the buffered image
return bimage;
}
// Draw a rounded box with a 2-pixel border
// Used on various card parts.
public static void drawRoundedBox(Graphics2D g, int x, int y, int w, int h, int bevel, Paint border, Color fill) {
g.setColor(new Color(0, 0, 0, 150));
g.drawOval(x-1, y-1, bevel*2, h);
g.setPaint(border);
g.drawOval(x, y, bevel*2-1, h-1);
g.drawOval(x + w - bevel*2, y, bevel*2-1, h-1);
g.drawOval(x+1, y+1, bevel*2-3, h-3);
g.drawOval(x+1 + w - bevel*2, y+1, bevel*2-3, h-3);
g.drawRect(x + bevel, y, w - 2*bevel, h-1);
g.drawRect(x+1 + bevel, y+1, w - 2*bevel-2, h-3);
g.setColor(fill);
g.fillOval(x+2, y+2, bevel*2-4, h-4);
g.fillOval(x+2 + w - bevel*2, y+2, bevel*2-4, h-4);
g.fillRect(x + bevel, y+2, w - 2*bevel, h-4);
}
// Get the width of a mana cost rendered with ManaSymbols.draw
public static int getManaCostWidth(String manaCost, int symbolSize) {
int width = 0;
manaCost = manaCost.replace("\\", "");
StringTokenizer tok = new StringTokenizer(manaCost, " ");
while (tok.hasMoreTokens()) {
tok.nextToken();
width += symbolSize;
}
return width;
}
// Abbreviate a piece of rules text, making substitutions to decrease its
// length. Also abbreviate reminder text.
private static Pattern abbreviationPattern;
private static Map<String, String> abbreviations = new HashMap<String, String>();
private static Pattern killReminderTextPattern;
static {
// Available abbreviations
abbreviations.put("enters the battlefield", "ETB");
abbreviations.put("less than", "<");
abbreviations.put("greater than", ">");
// Compile into regex
String patternString = "(";
Iterator<String> it = abbreviations.keySet().iterator();
while (it.hasNext()) {
patternString += it.next();
if (it.hasNext()) {
patternString += "|";
}
}
patternString += ")";
abbreviationPattern = Pattern.compile(patternString);
// Reminder text killing
killReminderTextPattern = Pattern.compile("\\([^\\)]*\\)");
}
public static String abbreviateRule(String rule) {
StringBuffer build = new StringBuffer();
Matcher match = abbreviationPattern.matcher(rule);
while (match.find()) {
match.appendReplacement(build, abbreviations.get(match.group(1)));
}
match.appendTail(build);
return build.toString();
}
public static String killReminderText(String rule) {
return killReminderTextPattern.matcher(rule).replaceAll("");
}
}

View file

@ -37,7 +37,7 @@ public class ManaSymbols {
private static boolean smallSymbolsFound = false;
private static boolean mediumSymbolsFound = false;
private static final Map<String, Image> setImages = new HashMap<>();
private static final Map<String, Map<String, Image>> setImages = new HashMap<>();
private static final Map<String, Dimension> setImagesExist = new HashMap<>();
private static final Pattern REPLACE_SYMBOLS_PATTERN = Pattern.compile("\\{([^}/]*)/?([^}]*)\\}");
private static String cachedPath;
@ -57,25 +57,32 @@ public class ManaSymbols {
return;
}
for (String set : setCodes) {
File file = new File(getSymbolsPath() + Constants.RESOURCE_PATH_SET + set + "-C.jpg");
try {
Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
int width = image.getWidth(null);
if (width > 21) {
int h = image.getHeight(null);
if (h > 0) {
Rectangle r = new Rectangle(21, (int) (h * 21.0f / width));
BufferedImage resized = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r);
setImages.put(set, resized);
}
} else {
setImages.put(set, image);
}
} catch (Exception e) {
}
String[] codes = new String[]{"C", "U", "R", "M"};
Map<String, Image> rarityImages = new HashMap<>();
setImages.put(set, rarityImages);
for (String rarityCode: codes) {
File file = new File(getSymbolsPath() + Constants.RESOURCE_PATH_SET + set + "-" + rarityCode + ".jpg");
try {
Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
int width = image.getWidth(null);
if (width > 21) {
int h = image.getHeight(null);
if (h > 0) {
Rectangle r = new Rectangle(21, (int) (h * 21.0f / width));
BufferedImage resized = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r);
rarityImages.put(set, resized);
}
} else {
rarityImages.put(rarityCode, image);
}
} catch (Exception e) {
}
}
try {
file = new File(getSymbolsPath() + Constants.RESOURCE_PATH_SET_SMALL);
File file = new File(getSymbolsPath() + Constants.RESOURCE_PATH_SET_SMALL);
if (!file.exists()) {
file.mkdirs();
}
@ -298,14 +305,27 @@ public class ManaSymbols {
}
public static Image getSetSymbolImage(String set) {
return setImages.get(set);
return getSetSymbolImage(set, "C");
}
public static Image getSetSymbolImage(String set, String rarity) {
Map<String, Image> rarityImages = setImages.get(set);
if (rarityImages != null) {
return rarityImages.get(rarity);
} else {
return null;
}
}
public static BufferedImage getSizedManaSymbol(String symbol) {
if (!manaImages.containsKey(GUISizeHelper.symbolDialogSize)) {
loadSymbolsImages(GUISizeHelper.symbolDialogSize);
return getSizedManaSymbol(symbol, GUISizeHelper.symbolDialogSize);
}
public static BufferedImage getSizedManaSymbol(String symbol, int size) {
if (!manaImages.containsKey(size)) {
loadSymbolsImages(size);
}
Map<String, BufferedImage> sizedSymbols = manaImages.get(GUISizeHelper.symbolDialogSize);
Map<String, BufferedImage> sizedSymbols = manaImages.get(size);
return sizedSymbols.get(symbol);
}
}

View file

@ -0,0 +1,934 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.card.arcane;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.LinearGradientPaint;
import java.awt.Paint;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.TexturePaint;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.font.TextMeasurer;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.text.CharacterIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.ImageIcon;
import mage.ObjectColor;
import mage.constants.CardType;
import mage.view.CardView;
import mage.view.PermanentView;
import org.apache.log4j.Logger;
import org.mage.card.arcane.CardRenderer;
import org.mage.card.arcane.CardRendererUtils;
import org.mage.card.arcane.ManaSymbols;
import org.mage.card.arcane.TextboxLoyaltyRule;
import org.mage.card.arcane.TextboxRule;
import org.mage.card.arcane.TextboxRuleType;
import sun.security.pkcs11.P11TlsKeyMaterialGenerator;
/*
private void cardRendererBasedRender(Graphics2D g) {
// Prepare for draw
g.translate(cardXOffset, cardYOffset);
int cardWidth = this.cardWidth - cardXOffset;
int cardHeight = this.cardHeight - cardYOffset;
// AA on
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Renderer
CardRenderer render = new ModernCardRenderer(gameCard, transformed);
Image img = imagePanel.getSrcImage();
if (img != null) {
render.setArtImage(img);
}
render.draw(g, cardWidth, cardHeight);
}
*/
/**
* @author stravant@gmail.com
*
* Base rendering class for new border cards
*/
public class ModernCardRenderer extends CardRenderer {
private static Logger LOGGER = Logger.getLogger(ModernCardRenderer.class);
///////////////////////////////////////////////////////////////////////////
// Textures for modern frame cards
private static TexturePaint loadBackgroundTexture(String name) {
URL url = ModernCardRenderer.class.getResource("/cardrender/background_texture_" + name + ".png");
ImageIcon icon = new ImageIcon(url);
BufferedImage img = CardRendererUtils.toBufferedImage(icon.getImage());
return new TexturePaint(img, new Rectangle(0, 0, img.getWidth(), img.getHeight()));
}
public static Paint BG_TEXTURE_WHITE = loadBackgroundTexture("white");
public static Paint BG_TEXTURE_BLUE = loadBackgroundTexture("blue");
public static Paint BG_TEXTURE_BLACK = loadBackgroundTexture("black");
public static Paint BG_TEXTURE_RED = loadBackgroundTexture("red");
public static Paint BG_TEXTURE_GREEN = loadBackgroundTexture("green");
public static Paint BG_TEXTURE_GOLD = loadBackgroundTexture("gold");
public static Paint BG_TEXTURE_ARTIFACT = loadBackgroundTexture("artifact");
public static Paint BG_TEXTURE_LAND = loadBackgroundTexture("land");
public static Color BORDER_WHITE = new Color(216, 203, 188);
public static Color BORDER_BLUE = new Color(20, 121, 175);
public static Color BORDER_BLACK = new Color(45, 45, 35);
public static Color BORDER_RED = new Color(201, 71, 58);
public static Color BORDER_GREEN = new Color(4, 136, 69);
public static Color BORDER_GOLD = new Color(255, 228, 124);
public static Color BORDER_COLORLESS = new Color(238, 242, 242);
public static Color BORDER_LAND = new Color(190, 173, 115);
public static Color BOX_WHITE = new Color(244, 245, 239);
public static Color BOX_BLUE = new Color(201, 223, 237);
public static Color BOX_BLACK = new Color(204, 194, 192);
public static Color BOX_RED = new Color(246, 208, 185);
public static Color BOX_GREEN = new Color(205, 221, 213);
public static Color BOX_GOLD = new Color(223, 195, 136);
public static Color BOX_COLORLESS = new Color(220, 228, 232);
public static Color BOX_LAND = new Color(220, 215, 213);
public static Color BOX_WHITE_NIGHT = new Color(169, 160, 145);
public static Color BOX_BLUE_NIGHT = new Color(46, 133, 176);
public static Color BOX_BLACK_NIGHT = new Color(95, 90, 89);
public static Color BOX_RED_NIGHT = new Color(188, 87, 57);
public static Color BOX_GREEN_NIGHT = new Color(31, 100, 44);
public static Color BOX_GOLD_NIGHT = new Color(171, 134, 70);
public static Color BOX_COLORLESS_NIGHT = new Color(118, 147, 158);
public static Color TEXTBOX_WHITE = new Color(252, 249, 244, 244);
public static Color TEXTBOX_BLUE = new Color(229, 238, 247, 244);
public static Color TEXTBOX_BLACK = new Color(241, 241, 240, 244);
public static Color TEXTBOX_RED = new Color(243, 224, 217, 244);
public static Color TEXTBOX_GREEN = new Color(217, 232, 223, 244);
public static Color TEXTBOX_GOLD = new Color(240, 234, 209, 244);
public static Color TEXTBOX_COLORLESS = new Color(219, 229, 233, 244);
public static Color TEXTBOX_LAND = new Color(218, 214, 212, 244);
public static Color ERROR_COLOR = new Color(255, 0, 255);
///////////////////////////////////////////////////////////////////////////
// Layout metrics for modern border cards
// How far the main box, art, and name / type line are inset from the
// card border. That is, the width of background texture that shows around
// the edge of the card.
protected int contentInset;
// Helper: The total inset from card edge to rules box etc.
// = borderWidth + contentInset
protected int totalContentInset;
// Width of the content region of the card
// = cardWidth - 2 x totalContentInset
protected int contentWidth;
// How tall the name / type lines and P/T box are
protected static float BOX_HEIGHT_FRAC = 0.065f; // x cardHeight
protected static int BOX_HEIGHT_MIN = 16;
protected int boxHeight;
// How far down the card is the type line placed?
protected static float TYPE_LINE_Y_FRAC = 0.57f; // x cardHeight
protected int typeLineY;
// How large is the box text, and how far is it down the boxes
protected int boxTextHeight;
protected int boxTextOffset;
protected Font boxTextFont;
// How large is the P/T text, and how far is it down the boxes
protected int ptTextHeight;
protected int ptTextOffset;
protected Font ptTextFont;
// Processed mana cost string
protected String manaCostString;
public ModernCardRenderer(CardView card, boolean isTransformed) {
// Pass off to parent
super(card, isTransformed);
// Mana cost string
manaCostString = ManaSymbols.getStringManaCost(cardView.getManaCost());
}
@Override
protected void layout(int cardWidth, int cardHeight) {
// Pass to parent
super.layout(cardWidth, cardHeight);
// Content inset, just equal to border width
contentInset = borderWidth;
// Total content inset helper
totalContentInset = borderWidth + contentInset;
// Content width
contentWidth = cardWidth - 2*totalContentInset;
// Box height
boxHeight = (int)Math.max(
BOX_HEIGHT_MIN,
BOX_HEIGHT_FRAC * cardHeight);
// Type line at
typeLineY = (int)(TYPE_LINE_Y_FRAC * cardHeight);
// Box text height
boxTextHeight = getTextHeightForBoxHeight(boxHeight);
boxTextOffset = (boxHeight - boxTextHeight)/2;
boxTextFont = new Font("Beleren", Font.PLAIN, boxTextHeight);
// Box text height
ptTextHeight = getPTTextHeightForLineHeight(boxHeight);
ptTextOffset = (boxHeight - ptTextHeight)/2;
ptTextFont = new Font("Beleren", Font.PLAIN, ptTextHeight);
}
@Override
protected void drawBorder(Graphics2D g) {
// Draw border as one rounded rectangle
g.setColor(Color.black);
g.fillRoundRect(0, 0, cardWidth, cardHeight, cornerRadius, cornerRadius);
}
@Override
protected void drawBackground(Graphics2D g) {
// Draw background, in 3 parts
if (cardView.isFaceDown()) {
// Just draw a brown rectangle
drawCardBack(g);
} else {
// Set texture to paint with
g.setPaint(getBackgroundPaint(cardView.getColor(), cardView.getCardTypes()));
// Draw main part (most of card)
g.fillRoundRect(
borderWidth, borderWidth,
cardWidth - borderWidth*2, cardHeight - borderWidth*4 - cornerRadius*2,
cornerRadius - 1, cornerRadius - 1);
// Draw the M15 rounded "swoosh" at the bottom
g.fillRoundRect(
borderWidth, cardHeight - borderWidth*4 - cornerRadius*4,
cardWidth - borderWidth*2, cornerRadius*4,
cornerRadius*2, cornerRadius*2);
// Draw the cutout into the "swoosh" for the textbox to lie over
g.fillRect(
borderWidth + contentInset, cardHeight - borderWidth*5,
cardWidth - borderWidth*2 - contentInset*2, borderWidth*2);
}
}
@Override
protected void drawArt(Graphics2D g) {
if (artImage != null) {
int imgWidth = artImage.getWidth();
int imgHeight = artImage.getHeight();
BufferedImage subImg =
artImage.getSubimage(
(int)(.079*imgWidth), (int)(.11*imgHeight),
(int)(.84*imgWidth), (int)(.42*imgHeight));
g.drawImage(subImg,
totalContentInset+1, totalContentInset+boxHeight,
contentWidth - 2, typeLineY - totalContentInset - boxHeight,
null);
}
}
@Override
protected void drawFrame(Graphics2D g) {
// Get the card colors to base the frame on
ObjectColor frameColors = getFrameObjectColor();
// Get the border paint
Color boxColor = getBoxColor(frameColors, cardView.getCardTypes(), isTransformed);
Paint textboxPaint = getTextboxPaint(frameColors, cardView.getCardTypes(), cardWidth);
Paint borderPaint = getBorderPaint(frameColors, cardView.getCardTypes(), cardWidth);
// Draw the main card content border
g.setPaint(borderPaint);
g.drawRect(
totalContentInset, totalContentInset,
contentWidth - 1, cardHeight - borderWidth*3 - totalContentInset - 1);
// Draw the textbox fill
g.setPaint(textboxPaint);
g.fillRect(
totalContentInset + 1, typeLineY,
contentWidth - 2, cardHeight - borderWidth*3 - typeLineY - 1);
// If it's a planeswalker, extend the textbox left border by some
if (cardView.getCardTypes().contains(CardType.PLANESWALKER)) {
g.setPaint(borderPaint);
g.fillRect(
totalContentInset, typeLineY + boxHeight,
cardWidth/16, cardHeight - typeLineY - boxHeight - borderWidth*3);
}
// Draw a shadow highlight at the right edge of the content frame
g.setColor(new Color(0, 0, 0, 100));
g.fillRect(
totalContentInset - 1, totalContentInset,
1, cardHeight - borderWidth*3 - totalContentInset - 1);
// Draw a shadow highlight separating the card art and rest of frame
g.drawRect(
totalContentInset + 1, totalContentInset + boxHeight,
contentWidth - 3, typeLineY - totalContentInset - boxHeight - 1);
// Draw the name line box
CardRendererUtils.drawRoundedBox(g,
borderWidth, totalContentInset,
cardWidth - 2*borderWidth, boxHeight,
contentInset,
borderPaint, boxColor);
// Draw the type line box
CardRendererUtils.drawRoundedBox(g,
borderWidth, typeLineY,
cardWidth - 2*borderWidth, boxHeight,
contentInset,
borderPaint, boxColor);
// Draw a small separator between the type line and box, and shadow
// at the left of the texbox, and above the name line
g.setColor(new Color(0, 0, 0, 150));
g.fillRect(
totalContentInset - 1, totalContentInset - 1,
contentWidth + 1, 1);
g.fillRect(
totalContentInset + 1, typeLineY + boxHeight,
contentWidth - 2, 1);
g.fillRect(
cardWidth - totalContentInset - 1, typeLineY + boxHeight,
1, cardHeight - borderWidth*3 - typeLineY - boxHeight);
// Draw the transform circle
int nameOffset = drawTransformationCircle(g, borderPaint);
// Draw the name line
drawNameLine(g,
totalContentInset + nameOffset, totalContentInset,
contentWidth - nameOffset, boxHeight);
// Draw the type line
drawTypeLine(g,
totalContentInset, typeLineY,
contentWidth, boxHeight);
// Draw the textbox rules
drawRulesText(g,
totalContentInset + 2, typeLineY + boxHeight + 2,
contentWidth - 4, cardHeight - typeLineY - boxHeight - 4 - borderWidth*3);
// Draw the bottom right stuff
drawBottomRight(g, borderPaint, boxColor);
}
// Draw the name line
protected void drawNameLine(Graphics2D g, int x, int y, int w, int h) {
// Width of the mana symbols
int manaCostWidth = CardRendererUtils.getManaCostWidth(manaCostString, boxTextHeight);
// Available width for name. Add a little bit of slop so that one character
// can partially go underneath the mana cost
int availableWidth = w - manaCostWidth + 2;
// Draw the name
AttributedString str = new AttributedString(cardView.getName());
str.addAttribute(TextAttribute.FONT, boxTextFont);
TextMeasurer measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext());
TextLayout layout = measure.getLayout(0, measure.getLineBreakIndex(0, availableWidth));
g.setColor(getBoxTextColor());
layout.draw(g, x, y + boxTextOffset + boxTextHeight - 1);
// Draw the mana symbols
ManaSymbols.draw(g, manaCostString, x + w - manaCostWidth, y + boxTextOffset, boxTextHeight);
}
// Draw the type line (color indicator, types, and expansion symbol)
protected void drawTypeLine(Graphics2D g, int x, int y, int w, int h) {
// Draw expansion symbol
int expansionSymbolWidth = drawExpansionSymbol(g, x, y, w, h);
// Draw type line text
int availableWidth = w - expansionSymbolWidth + 1;
String types = getCardTypeLine();
g.setFont(boxTextFont);
// Replace "Legendary" in type line if there's not enough space
if (g.getFontMetrics().stringWidth(types) > availableWidth) {
types = types.replace("Legendary", "L.");
}
AttributedString str = new AttributedString(types);
str.addAttribute(TextAttribute.FONT, boxTextFont);
TextMeasurer measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext());
TextLayout layout = measure.getLayout(0, measure.getLineBreakIndex(0, availableWidth));
g.setColor(getBoxTextColor());
layout.draw(g, x, y + boxTextOffset + boxTextHeight - 1);
}
// Draw the P/T and/or Loyalty boxes
protected void drawBottomRight(Graphics2D g, Paint borderPaint, Color fill) {
// Where to start drawing the things
int curY = cardHeight - (int)(0.03f*cardHeight);
// Width of the boxes
int partWidth = (int)Math.max(30, 0.20f*cardWidth);
// Is it a creature?
if (cardView.getCardTypes().contains(CardType.CREATURE)) {
int x = cardWidth - borderWidth - partWidth;
// Draw PT box
CardRendererUtils.drawRoundedBox(g,
x, curY - boxHeight,
partWidth, boxHeight,
contentInset,
borderPaint,
fill);
// Draw shadow line top
g.setColor(new Color(0, 0, 0, 150));
g.fillRect(
x + contentInset, curY - boxHeight - 1,
partWidth - 2*contentInset, 1);
// Draw text
g.setColor(getBoxTextColor());
g.setFont(ptTextFont);
String ptText = cardView.getPower() + "/" + cardView.getToughness();
int ptTextWidth = g.getFontMetrics().stringWidth(ptText);
g.drawString(ptText,
x + (partWidth - ptTextWidth)/2, curY - ptTextOffset - 1);
// Does it have damage on it?
if ((cardView instanceof PermanentView) && ((PermanentView)cardView).getDamage() > 0) {
// Show marked damage
}
curY -= boxHeight;
}
// Is it a walker?
if (cardView.getCardTypes().contains(CardType.PLANESWALKER)) {
// Draw the PW loyalty box
int w = partWidth;
int h = partWidth/2;
int x = cardWidth - partWidth - borderWidth;
int y = curY - h;
Polygon symbol = new Polygon(
new int[]{
x + w/2,
(int)(x + w*0.9),
x + w,
(int)(x + w*0.6),
x + w/2,
(int)(x + w*0.4),
x,
(int)(x + w*0.1),
},
new int[]{
y + h,
(int)(y + 0.8*h),
y,
(int)(y - 0.2*h),
y,
(int)(y - 0.2*h),
y,
(int)(y + 0.8*h),
},
8);
// Draw + stroke
g.setColor(Color.black);
g.fillPolygon(symbol);
g.setColor(new Color(200, 200, 200));
g.setStroke(new BasicStroke(2));
g.drawPolygon(symbol);
g.setStroke(new BasicStroke(1));
// Loyalty number
String loyalty;
if (cardView instanceof PermanentView) {
loyalty = cardView.getLoyalty();
} else {
loyalty = cardView.getStartingLoyalty();
}
g.setFont(ptTextFont);
g.setColor(Color.white);
int loyaltyWidth = g.getFontMetrics().stringWidth(loyalty);
g.drawString(loyalty, x + (w - loyaltyWidth)/2, y + ptTextHeight + (h - ptTextHeight)/2);
// Advance
curY -= (int)(1.2*y);
}
}
// Draw the card's textbox in a given rect
protected boolean loyaltyAbilityColorToggle = false;
protected void drawRulesText(Graphics2D g, int x, int y, int w, int h) {
// Initial font size to try to render at
Font font = new Font("Arial", Font.PLAIN, 12);
Font fontItalic = new Font("Arial", Font.ITALIC, 12);
// Handle the keyword rules
boolean hasKeywords = !textboxKeywords.isEmpty();
String keywordRulesString = getKeywordRulesString();
AttributedString keywordRulesAttributed = new AttributedString(keywordRulesString);
if (hasKeywords) {
keywordRulesAttributed.addAttribute(TextAttribute.FONT, font);
}
// Get the total height
List<AttributedString> attributedRules = new ArrayList<>();
boolean useSmallFont = false;
int remaining = h;
{
if (hasKeywords) {
remaining -= drawSingleRule(g, keywordRulesAttributed, null, 0, 0, w, remaining, false);
}
for (TextboxRule rule: textboxRules) {
AttributedString attributed = rule.generateAttributedString(font, fontItalic);
attributedRules.add(attributed);
remaining -= drawSingleRule(g, attributed, rule, 0, 0, w, remaining, false);
if (remaining < 0) {
useSmallFont = true;
break;
}
}
}
// If there wasn't enough room, try using a smaller font
if (useSmallFont) {
font = new Font("Arial", Font.PLAIN, 9);
fontItalic = new Font("Arial", Font.ITALIC, 9);
if (hasKeywords) {
keywordRulesAttributed = new AttributedString(keywordRulesString);
keywordRulesAttributed.addAttribute(TextAttribute.FONT, font);
}
// Clear out the attributed rules and reatribute them with the new font size
attributedRules.clear();
for (TextboxRule rule: textboxRules) {
AttributedString attributed = rule.generateAttributedString(font, fontItalic);
attributedRules.add(attributed);
}
}
// Do we have room for additional spacing between the parts of text?
// If so, calculate the spacing based on how much space was left over
int spacing;
if (useSmallFont) {
spacing = 0;
} else {
spacing = (int)(remaining / (hasKeywords ? (textboxRules.size() + 2) : (textboxRules.size() + 1)));
}
// Do the actual draw
loyaltyAbilityColorToggle = false;
g.setColor(Color.black);
int curY = y + spacing;
if (hasKeywords) {
int adv = drawSingleRule(g, keywordRulesAttributed, null, x, y, w, h, true);
curY += adv + spacing;
h -= adv;
}
for (int i = 0; i < textboxRules.size(); ++i) {
TextboxRule rule = textboxRules.get(i);
AttributedString attributedRule = attributedRules.get(i);
int adv = drawSingleRule(g, attributedRule, rule, x, curY, w, h, true);
curY += adv + spacing;
h -= adv;
if (h < 0) {
break;
}
}
}
// Get the first line of the textbox, the keyword string
private String getKeywordRulesString() {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < textboxKeywords.size(); ++i) {
builder.append(textboxKeywords.get(i).text);
if (i != textboxKeywords.size() - 1) {
builder.append(", ");
}
}
return builder.toString();
}
// Draw a single rule and returns the amount vertically advanced by, but
// only if doDraw is true. If doDraw is false, just returns the vertical
// advance if the rule were to be drawn.
private int drawSingleRule(Graphics2D g, AttributedString text, TextboxRule rule, int x, int y, int w, int h, boolean doDraw) {
// Inset, in case we are a leveler or loyalty ability
int inset = 0;
if (rule != null && rule.type == TextboxRuleType.LOYALTY) {
inset = cardWidth/12;
}
int availWidth = w - inset;
FontRenderContext frc = g.getFontRenderContext();
AttributedCharacterIterator textIter = text.getIterator();
LineBreakMeasurer measure = new LineBreakMeasurer(textIter, frc);
float yPos = y;
float remain = h;
AttributedCharacterIterator newLineCheck = text.getIterator();
while (measure.getPosition() < textIter.getEndIndex()) {
// Advance iterator to next line break
char ch = newLineCheck.setIndex(measure.getPosition());
while ((ch = newLineCheck.next()) != CharacterIterator.DONE) {
if (ch == '\n') {
break;
}
}
// Get the text layout
TextLayout layout = measure.nextLayout(availWidth, newLineCheck.getIndex(), false);
float ascent = layout.getAscent();
yPos += ascent;
remain -= ascent;
if (remain < 0) {
break;
}
if (doDraw) {
g.setColor(Color.black);
layout.draw(g, x + inset, yPos);
}
yPos += layout.getDescent() + layout.getLeading() - 2;
}
// Advance
int advance = ((int)Math.ceil(yPos)) - y;
// Is it a loyalty ability?
if (rule != null && rule.type == TextboxRuleType.LOYALTY) {
TextboxLoyaltyRule loyaltyRule = (TextboxLoyaltyRule)rule;
Polygon symbol;
int symbolWidth = (x + inset) - borderWidth - 4;
int symbolHeight = (int)(0.7f*symbolWidth);
if (symbolHeight > advance) {
advance = symbolHeight;
}
int symbolX = x - borderWidth;
int symbolY = y + (advance - symbolHeight)/2;
if (doDraw) {
if (loyaltyRule.loyaltyChange < 0 || loyaltyRule.loyaltyChange == TextboxLoyaltyRule.MINUS_X) {
symbol = new Polygon(
new int[]{
symbolX,
symbolX + symbolWidth,
symbolX + symbolWidth,
symbolX + symbolWidth/2,
symbolX,
},
new int[]{
symbolY,
symbolY,
symbolY + symbolHeight - 3,
symbolY + symbolHeight + 3,
symbolY + symbolHeight - 3,
},
5);
} else if (loyaltyRule.loyaltyChange > 0) {
symbol = new Polygon(
new int[]{
symbolX,
symbolX + symbolWidth/2,
symbolX + symbolWidth,
symbolX + symbolWidth,
symbolX,
},
new int[]{
symbolY + 3,
symbolY - 3,
symbolY + 3,
symbolY + symbolHeight,
symbolY + symbolHeight,
},
5);
} else {
symbol = new Polygon(
new int[]{
symbolX,
symbolX + symbolWidth,
symbolX + symbolWidth,
symbolX,
},
new int[]{
symbolY,
symbolY,
symbolY + symbolHeight,
symbolY + symbolHeight,
},
4);
}
g.setColor(new Color(0, 0, 0, 128));
g.fillRect(x+2, y+advance+1, w-2, 1);
g.setColor(Color.black);
g.fillPolygon(symbol);
g.setColor(new Color(200, 200, 200));
g.setStroke(new BasicStroke(2));
g.drawPolygon(symbol);
g.setStroke(new BasicStroke(1));
g.setColor(Color.white);
g.setFont(boxTextFont);
String loyaltyString = loyaltyRule.getChangeString();
int textWidth = g.getFontMetrics().stringWidth(loyaltyString);
g.drawString(loyaltyString,
symbolX + (symbolWidth - textWidth)/2,
symbolY + symbolHeight - (symbolHeight - boxTextHeight)/2);
advance += 3;
loyaltyAbilityColorToggle = !loyaltyAbilityColorToggle;
}
}
return advance;
}
// Draw the transformation circle if there is one, and return the
// horizontal width taken up into the content space by it.
protected boolean isNightCard() {
return isTransformed;
}
protected boolean isTransformCard() {
return cardView.canTransform() || isTransformed;
}
protected int drawTransformationCircle(Graphics2D g, Paint borderPaint) {
int transformCircleOffset = 0;
if (isTransformCard()) {
transformCircleOffset = boxHeight - contentInset;
g.setPaint(borderPaint);
g.drawOval(borderWidth, totalContentInset, boxHeight - 1, boxHeight - 1);
g.setColor(Color.black);
g.fillOval(borderWidth+1, totalContentInset+1, boxHeight-2, boxHeight-2);
g.setColor(Color.white);
if (isNightCard()) {
g.fillArc(borderWidth+3, totalContentInset+3, boxHeight-6, boxHeight-6, 90, 270);
g.setColor(Color.black);
g.fillArc(borderWidth+3+3, totalContentInset+3, boxHeight-6-3, boxHeight-6, 90, 270);
} else {
g.fillOval(borderWidth+3, totalContentInset+3, boxHeight-6, boxHeight-6);
}
}
return transformCircleOffset;
}
// Get the text height for a given box height
protected static int getTextHeightForBoxHeight(int h) {
if (h < 15) {
return h-3;
} else {
return (int)Math.ceil(.6*h);
}
}
protected static int getPTTextHeightForLineHeight(int h) {
return h - 4;
}
// Determine the color of the name / type line text
protected Color getBoxTextColor() {
if (isNightCard()) {
return Color.white;
} else {
return Color.black;
}
}
// Determine the colors to base the frame on
protected ObjectColor getFrameObjectColor() {
// TODO: Take into account devoid, land frame colors, etc
return cardView.getColor().union(cardView.getFrameColor());
}
// Determine which background paint to use from a set of colors
// and the current card.
protected static Paint getBackgroundPaint(ObjectColor colors, Collection<CardType> types) {
if (types.contains(CardType.LAND)) {
return BG_TEXTURE_LAND;
} else if (types.contains(CardType.ARTIFACT)) {
return BG_TEXTURE_ARTIFACT;
} else if (colors.isMulticolored()) {
return BG_TEXTURE_GOLD;
} else if (colors.isWhite()) {
return BG_TEXTURE_WHITE;
} else if (colors.isBlue()) {
return BG_TEXTURE_BLUE;
} else if (colors.isBlack()) {
return BG_TEXTURE_BLACK;
} else if (colors.isRed()) {
return BG_TEXTURE_RED;
} else if (colors.isGreen()) {
return BG_TEXTURE_GREEN;
} else {
// Colorless
return new Color(71, 86, 101);
}
}
// Get the box color for the given colors
protected static Color getBoxColor(ObjectColor colors, Collection<CardType> types, boolean isNightCard) {
if (colors.getColorCount() == 2 && types.contains(CardType.LAND)) {
// Special case for two color lands. Boxes should be normal land colored
// rather than multicolor. Three or greater color lands use a multi-color
// box as normal.
return BOX_LAND;
} else if (colors.isMulticolored()) {
return isNightCard ? BOX_GOLD_NIGHT : BOX_GOLD;
} else if (colors.isColorless()) {
if (types.contains(CardType.LAND)) {
return BOX_LAND;
} else {
return isNightCard ? BOX_COLORLESS_NIGHT : BOX_COLORLESS;
}
} else if (colors.isWhite()) {
return isNightCard ? BOX_WHITE_NIGHT : BOX_WHITE;
} else if (colors.isBlue()) {
return isNightCard ? BOX_BLUE_NIGHT : BOX_BLUE;
} else if (colors.isBlack()) {
return isNightCard ? BOX_BLACK_NIGHT : BOX_BLACK;
} else if (colors.isRed()) {
return isNightCard ? BOX_RED_NIGHT : BOX_RED;
} else if (colors.isGreen()) {
return isNightCard ? BOX_GREEN_NIGHT : BOX_GREEN;
} else {
return ERROR_COLOR;
}
}
// Get the border color for a single color
protected static Color getBorderColor(ObjectColor color) {
if (color.isWhite()) {
return BORDER_WHITE;
} else if (color.isBlue()) {
return BORDER_BLUE;
} else if (color.isBlack()) {
return BORDER_BLACK;
} else if (color.isRed()) {
return BORDER_RED;
} else if (color.isGreen()) {
return BORDER_GREEN;
} else {
return ERROR_COLOR;
}
}
// Determine the border paint to use, based on an ObjectColors
protected static Paint getBorderPaint(ObjectColor colors, Collection<CardType> types, int width) {
if (colors.isMulticolored()) {
if (colors.getColorCount() == 2) {
List<ObjectColor> twoColors = colors.getColors();
// Two-color frames look better if we use a whiter white
// than the normal white frame color for them, as the normal
// white border color is very close to the gold background
// color.
Color color1, color2;
if (twoColors.get(0).isWhite()) {
color1 = new Color(240, 240, 240);
} else {
color1 = getBorderColor(twoColors.get(0));
}
if (twoColors.get(1).isWhite()) {
color2 = new Color(240, 240, 240);
} else {
color2 = getBorderColor(twoColors.get(1));
}
// Special case for two colors, gradient paint
return new LinearGradientPaint(
0, 0, width, 0,
new float[]{0.4f, 0.6f},
new Color[]{color1, color2});
} else {
return BORDER_GOLD;
}
} else if (colors.isColorless()) {
if (types.contains(CardType.LAND)) {
return BORDER_LAND;
} else {
return BORDER_COLORLESS;
}
} else {
return getBorderColor(colors);
}
}
// Determine the textbox color for a single color
protected static Color getTextboxColor(ObjectColor color) {
if (color.isWhite()) {
return TEXTBOX_WHITE;
} else if (color.isBlue()) {
return TEXTBOX_BLUE;
} else if (color.isBlack()) {
return TEXTBOX_BLACK;
} else if (color.isRed()) {
return TEXTBOX_RED;
} else if (color.isGreen()) {
return TEXTBOX_GREEN;
} else {
return ERROR_COLOR;
}
}
// Determine the border paint to use, based on an ObjectColors
protected static Paint getTextboxPaint(ObjectColor colors, Collection<CardType> types, int width) {
if (colors.isMulticolored()) {
if (colors.getColorCount() == 2) {
List<ObjectColor> twoColors = colors.getColors();
// Special case for two colors, gradient paint
return new LinearGradientPaint(
0, 0, width, 0,
new float[]{0.4f, 0.6f},
new Color[]{
getTextboxColor(twoColors.get(0)),
getTextboxColor(twoColors.get(1))
});
} else {
return TEXTBOX_GOLD;
}
} else if (colors.isColorless()) {
if (types.contains(CardType.LAND)) {
return TEXTBOX_LAND;
} else {
return TEXTBOX_COLORLESS;
}
} else {
return getTextboxColor(colors);
}
}
}

View file

@ -0,0 +1,17 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.card.arcane;
import java.util.List;
/**
* @author stravant@gmail.com
*/
public class TextboxKeywordRule extends TextboxRule {
public TextboxKeywordRule(String text, List<AttributeRegion> regions) {
super(text, regions, TextboxRuleType.SIMPLE_KEYWORD);
}
}

View file

@ -0,0 +1,28 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.card.arcane;
import java.text.AttributedString;
import java.util.List;
/**
* @author StravantUser
*
* Level rule associated with leveler cards
*/
public class TextboxLevelRule extends TextboxRule {
// The levels that this rule applies to
public int levelFrom;
public int levelTo;
public static int AND_HIGHER = 100;
public TextboxLevelRule(String text, List<AttributeRegion> regions, int levelFrom, int levelTo) {
super(text, regions, TextboxRuleType.LEVEL);
this.levelFrom = levelFrom;
this.levelTo = levelTo;
}
}

View file

@ -0,0 +1,33 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.card.arcane;
import java.text.AttributedString;
import java.util.List;
/**
* @author StravantUser
*/
public class TextboxLoyaltyRule extends TextboxRule {
public int loyaltyChange;
public static int MINUS_X = 100;
public String getChangeString() {
if (loyaltyChange == MINUS_X) {
return "-X";
} else if (loyaltyChange > 0) {
return "+" + loyaltyChange;
} else {
return "" + loyaltyChange;
}
}
public TextboxLoyaltyRule(String text, List<AttributeRegion> regions, int loyaltyChange) {
super(text, regions, TextboxRuleType.LOYALTY);
this.loyaltyChange = loyaltyChange;
}
}

View file

@ -0,0 +1,94 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.card.arcane;
import java.awt.Font;
import java.awt.Image;
import java.awt.font.GraphicAttribute;
import java.awt.font.ImageGraphicAttribute;
import java.awt.font.TextAttribute;
import java.text.AttributedString;
import java.util.List;
/**
* @author stravant@gmail.com
*
* Class describing parsed & translated rules in the text box of a card,
* ready to be rendered.
*/
public class TextboxRule {
// An attributed region in the text, which can be applied to an
// attributed string.
public interface AttributeRegion {
public void applyToAttributedString(AttributedString str, Font normal, Font italic);
}
// A region of italics, or bold text in a
public static class ItalicRegion implements AttributeRegion {
ItalicRegion(int start, int end) {
this.start = start;
this.end = end;
}
private final int start;
private final int end;
@Override
public void applyToAttributedString(AttributedString str, Font normal, Font italic) {
if (end > start+1) {
str.addAttribute(TextAttribute.FONT, italic, start, end);
}
}
}
// A special symbol embedded at some point in a string
public static class EmbeddedSymbol implements AttributeRegion {
EmbeddedSymbol(String symbol, int location) {
this.symbol = symbol;
this.location = location;
}
private final String symbol;
private final int location;
@Override
public void applyToAttributedString(AttributedString str, Font normal, Font italic) {
Image symbolImage = ManaSymbols.getSizedManaSymbol(symbol, normal.getSize());
if (symbolImage != null) {
ImageGraphicAttribute imgAttr =
new ImageGraphicAttribute(symbolImage, GraphicAttribute.BOTTOM_ALIGNMENT);
str.addAttribute(TextAttribute.CHAR_REPLACEMENT, imgAttr, location, location+1);
}
}
}
public String text;
public TextboxRuleType type;
private List<AttributeRegion> regions;
protected TextboxRule(String text, List<AttributeRegion> regions, TextboxRuleType type) {
this.text = text;
this.type = type;
this.regions = regions;
}
public TextboxRule(String text, List<AttributeRegion> regions) {
this(text, regions, TextboxRuleType.NORMAL);
}
public AttributedString generateAttributedString(Font normal, Font italic) {
// Build the final attributed text using the regions
// Do it in reverse order for proper handling of regions where
// there are multiple attributes stacked (EG: bold + italic)
AttributedString attributedRule = new AttributedString(text);
if (text.length() != 0) {
attributedRule.addAttribute(TextAttribute.FONT, normal);
for (int i = regions.size()-1; i >= 0; --i) {
regions.get(i).applyToAttributedString(attributedRule, normal, italic);
}
}
return attributedRule;
}
}

View file

@ -0,0 +1,250 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.card.arcane;
import java.awt.Font;
import java.awt.Image;
import java.awt.font.GraphicAttribute;
import java.awt.font.ImageGraphicAttribute;
import java.awt.font.TextAttribute;
import java.text.AttributedString;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import mage.view.CardView;
import org.apache.log4j.Logger;
/**
*
* @author StravantUser
*/
public class TextboxRuleParser {
private static final Logger LOGGER = Logger.getLogger(CardPanel.class);
private static final Pattern LevelAbilityPattern = Pattern.compile("Level (\\d+)-?(\\d*)(\\+?)");
private static final Pattern LoyaltyAbilityPattern = Pattern.compile("^(\\+|\\-)(\\d+|X): ");
private static final Pattern SimpleKeywordPattern = Pattern.compile("^(\\w+( \\w+)?)\\s*(\\([^\\)]*\\))?\\s*$");
// Parse a given rule (given as a string) into a TextboxRule, replacing
// symbol annotations, italics, etc, parsing out information such as
// if the ability is a loyalty ability, and returning an TextboxRule
// representing that information, which can be used to render the rule in
// the textbox of a card.
public static TextboxRule parse(CardView source, String rule) {
// List of regions to apply
ArrayList<TextboxRule.AttributeRegion> regions = new ArrayList<>();
// Leveler / loyalty
boolean isLeveler = false;
int levelFrom = 0;
int levelTo = 0;
boolean isLoyalty = false;
int loyaltyChange = 0;
// Parse the attributedString contents
int index = 0;
int outputIndex = 0;
// Is it a simple keyword ability?
{
Matcher simpleKeywordMatch = SimpleKeywordPattern.matcher(rule);
if (simpleKeywordMatch.find()) {
return new TextboxKeywordRule(simpleKeywordMatch.group(1), regions);
}
}
// Check if it's a loyalty ability. Must be right at the start of the rule
{
Matcher loyaltyMatch = LoyaltyAbilityPattern.matcher(rule);
if (loyaltyMatch.find()) {
// Get the loyalty change
if (loyaltyMatch.group(2).equals("X")) {
loyaltyChange = TextboxLoyaltyRule.MINUS_X;
} else {
loyaltyChange = Integer.parseInt(loyaltyMatch.group(2));
if (loyaltyMatch.group(1).equals("-")) {
loyaltyChange = -loyaltyChange;
}
}
isLoyalty = true;
// Go past the match
index = loyaltyMatch.group().length();
}
}
Deque<Integer> openingStack = new ArrayDeque<>();
StringBuilder build = new StringBuilder();
while (index < rule.length()) {
int initialIndex = index;
char ch = rule.charAt(index);
if (ch == '{') {
// Handling for `{this}`
int closeIndex = rule.indexOf('}', index);
if (closeIndex == -1) {
// Malformed input, nothing to do
++index;
++outputIndex;
build.append(ch);
} else {
String contents = rule.substring(index+1, closeIndex);
if (contents.equals("this") || contents.equals("source")) {
// Replace {this} with the card's name
String cardName = source.getName();
build.append(cardName);
index += contents.length() + 2;
outputIndex += cardName.length();
} else {
Image symbol = ManaSymbols.getSizedManaSymbol(contents, 10);
if (symbol != null) {
// Mana or other inline symbol
build.append('#');
regions.add(new TextboxRule.EmbeddedSymbol(contents, outputIndex));
++outputIndex;
index = closeIndex+1;
} else {
// Bad entry
build.append('{');
build.append(contents);
build.append('}');
index = closeIndex+1;
outputIndex += (contents.length() + 2);
}
}
}
} else if (ch == '&') {
// Handling for `&mdash;`
if (rule.startsWith("&mdash;", index)) {
build.append('—');
index += 7;
++outputIndex;
} else if (rule.startsWith("&bull", index)) {
build.append('•');
index += 5;
++outputIndex;
} else {
LOGGER.error("Bad &...; sequence `" + rule.substring(index+1, index+10) + "` in rule.");
build.append('&');
++index;
++outputIndex;
}
} else if (ch == '<') {
// Handling for `<i>` and `<br/>`
int closeIndex = rule.indexOf('>', index);
if (closeIndex != -1) {
// Is a tag
String tag = rule.substring(index+1, closeIndex);
if (tag.charAt(tag.length()-1) == '/') {
// Pure closing tag (like <br/>)
if (tag.equals("br/")) {
build.append('\n');
++outputIndex;
} else {
// Unknown
build.append('<').append(tag).append('>');
outputIndex += (tag.length() + 2);
}
} else if (tag.charAt(0) == '/') {
// Opening index for the tag
int openingIndex;
if (openingStack.isEmpty()) {
// Malformed input, just make an empty interval
openingIndex = outputIndex;
} else {
openingIndex = openingStack.pop();
}
// What tag is it?
if (tag.equals("/i")) {
// Italics
regions.add(new TextboxRule.ItalicRegion(openingIndex, outputIndex));
} else if (tag.equals("/b")) {
// Bold, see if it's a level ability
String content = build.substring(openingIndex);
Matcher levelMatch = LevelAbilityPattern.matcher(content);
if (levelMatch.find()) {
try {
levelFrom = Integer.parseInt(levelMatch.group(1));
if (!levelMatch.group(2).equals("")) {
levelTo = Integer.parseInt(levelMatch.group(2));
}
if (!levelMatch.group(3).equals("")) {
levelTo = TextboxLevelRule.AND_HIGHER;
}
isLeveler = true;
} catch (Exception e) {
LOGGER.error("Bad leveler levels in rule `" + rule + "`.");
}
}
} else {
// Unknown
build.append('<').append(tag).append('>');
outputIndex += (tag.length() + 2);
}
} else {
// Is it a <br> tag special case? [Why can't it have a closing `/`... =( ]
if (tag.equals("br")) {
build.append('\n');
++outputIndex;
} else {
// Opening tag
openingStack.push(outputIndex);
}
}
// Skip characters
index = closeIndex+1;
} else {
// Malformed tag
build.append('<');
++outputIndex;
++index;
}
} else {
// Normal character
++index;
++outputIndex;
build.append(ch);
}
if (outputIndex != build.length()) {
// Somehow our parsing code output symbols but didn't update the output index correspondingly
LOGGER.error("The human is dead; mismatch! Failed on rule: `" + rule + "` due to not updating outputIndex properly.");
// Bail out
build = new StringBuilder(rule);
regions.clear();
break;
}
if (index == initialIndex) {
// Somehow our parsing failed to consume the
LOGGER.error("Failed on rule `" + rule + "` due to not consuming a character.");
// Bail out
build = new StringBuilder(rule);
regions.clear();
break;
}
}
// Build and return the rule
rule = build.toString();
if (isLoyalty) {
return new TextboxLoyaltyRule(rule, regions, loyaltyChange);
} else if (isLeveler) {
return new TextboxLevelRule(rule, regions, levelFrom, levelTo);
} else {
return new TextboxRule(rule, regions);
}
}
}

View file

@ -0,0 +1,25 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.card.arcane;
/**
* @author stravant@gmail.com
*/
public enum TextboxRuleType {
/* Normal abilities, just rendered as lines of text with embedded symbols
* replaced to the relevant images. */
NORMAL,
/* Keyword ability. To be displayed in the comma separated list at the
* very top of the rules box */
SIMPLE_KEYWORD,
/* Loyalty abilities on planeswalkers */
LOYALTY,
/* Levelup creature - static ability at a given level */
LEVEL
}

View file

@ -17,6 +17,7 @@ import javax.swing.JDialog;
import javax.swing.JLayeredPane;
import mage.cards.MagePermanent;
import mage.cards.action.ActionCallback;
import mage.client.dialog.PreferencesDialog;
import mage.client.util.GUISizeHelper;
import mage.constants.Rarity;
import mage.interfaces.plugin.CardPlugin;
@ -31,6 +32,7 @@ import net.xeoh.plugins.base.annotations.meta.Author;
import org.apache.log4j.Logger;
import org.mage.card.arcane.Animation;
import org.mage.card.arcane.CardPanel;
import org.mage.card.arcane.CardPanelComponentImpl;
import org.mage.card.arcane.ManaSymbols;
import org.mage.plugins.card.dl.DownloadGui;
import org.mage.plugins.card.dl.DownloadJob;
@ -41,6 +43,7 @@ import org.mage.plugins.card.dl.sources.GathererSets;
import org.mage.plugins.card.dl.sources.GathererSymbols;
import org.mage.plugins.card.images.ImageCache;
import org.mage.plugins.card.info.CardInfoPaneImpl;
import org.mage.card.arcane.CardPanelRenderImpl;
/**
* {@link CardPlugin} implementation.
@ -106,9 +109,22 @@ public class CardPluginImpl implements CardPlugin {
cardWidthMax = (int) GUISizeHelper.battlefieldCardMaxDimension.getWidth();
}
/**
* Temporary card rendering shim. Split card rendering isn't implemented yet, so
* use old component based rendering for the split cards.
*/
private CardPanel makePanel(CardView view, UUID gameId, boolean loadImage, ActionCallback callback, boolean isFoil, Dimension dimension) {
String fallback = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_FALLBACK, "false");
if (view.isSplitCard() || fallback.equals("true")) {
return new CardPanelComponentImpl(view, gameId, loadImage, callback, isFoil, dimension);
} else {
return new CardPanelRenderImpl(view, gameId, loadImage, callback, isFoil, dimension);
}
}
@Override
public MagePermanent getMagePermanent(PermanentView permanent, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage) {
CardPanel cardPanel = new CardPanel(permanent, gameId, loadImage, callback, false, dimension);
CardPanel cardPanel = makePanel(permanent, gameId, loadImage, callback, false, dimension);
boolean implemented = !permanent.getRarity().equals(Rarity.NA);
cardPanel.setShowCastingCost(implemented);
return cardPanel;
@ -116,7 +132,7 @@ public class CardPluginImpl implements CardPlugin {
@Override
public MagePermanent getMageCard(CardView cardView, Dimension dimension, UUID gameId, ActionCallback callback, boolean canBeFoil, boolean loadImage) {
CardPanel cardPanel = new CardPanel(cardView, gameId, loadImage, callback, false, dimension);
CardPanel cardPanel = makePanel(cardView, gameId, loadImage, callback, false, dimension);
boolean implemented = cardView.getRarity() != null && !cardView.getRarity().equals(Rarity.NA);
cardPanel.setShowCastingCost(implemented);
return cardPanel;

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

View file

@ -0,0 +1,28 @@
package mage.cards;
/**
* @author stravant@gmail.com
*
* Enum listing the possible card faces for a card
*
* Because of Time Spiral block's shifted cards it is
* not sufficient to just look at a card's edition to
* determine what the card face should be.
*/
public enum CardBorder {
/* Old border card frames. ALPHA -> 8th ED */
OLD,
/* Future Sight frames. FUT futureshifted */
FUT,
/* Planar Chaos frames. PLC planeshifted */
PLC,
/* Modern card frames. 8th ED -> M15 */
MOD,
/* New border cards, M15 -> current */
M15
}

View file

@ -32,8 +32,10 @@ import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.SpellAbility;
import mage.abilities.common.PlanswalkerEntersWithLoyalityCountersAbility;
import mage.abilities.costs.mana.ManaCosts;
import mage.cards.Card;
import mage.cards.SplitCard;
@ -68,10 +70,12 @@ public class CardView extends SimpleCardView {
protected String power;
protected String toughness;
protected String loyalty;
protected String startingLoyalty;
protected List<CardType> cardTypes;
protected List<String> subTypes;
protected List<String> superTypes;
protected ObjectColor color;
protected ObjectColor frameColor;
protected List<String> manaCost;
protected int convertedManaCost;
protected Rarity rarity;
@ -329,6 +333,12 @@ public class CardView extends SimpleCardView {
}
}
}
// Frame color
this.frameColor = card.getFrameColor(game);
// Get starting loyalty
this.startingLoyalty = "" + card.getStartingLoyalty();
}
public CardView(MageObject object) {
@ -373,6 +383,10 @@ public class CardView extends SimpleCardView {
this.expansionSetCode = stackAbility.getExpansionSetCode();
}
}
// Frame color
this.frameColor = object.getFrameColor(null);
// Starting loyalty. Must be extracted from an ability
this.startingLoyalty = "" + object.getStartingLoyalty();
}
protected CardView() {
@ -407,10 +421,12 @@ public class CardView extends SimpleCardView {
this.power = "";
this.toughness = "";
this.loyalty = "";
this.startingLoyalty = "";
this.cardTypes = new ArrayList<>();
this.subTypes = new ArrayList<>();
this.superTypes = new ArrayList<>();
this.color = new ObjectColor();
this.frameColor = new ObjectColor();
this.manaCost = new ArrayList<>();
this.convertedManaCost = 0;
@ -451,10 +467,12 @@ public class CardView extends SimpleCardView {
this.power = token.getPower().toString();
this.toughness = token.getToughness().toString();
this.loyalty = "";
this.startingLoyalty = "";
this.cardTypes = token.getCardType();
this.subTypes = token.getSubtype();
this.superTypes = token.getSupertype();
this.color = token.getColor(null);
this.frameColor = token.getFrameColor(null);
this.manaCost = token.getManaCost().getSymbols();
this.rarity = Rarity.NA;
this.type = token.getTokenType();
@ -518,6 +536,10 @@ public class CardView extends SimpleCardView {
return loyalty;
}
public String getStartingLoyalty() {
return startingLoyalty;
}
public List<CardType> getCardTypes() {
return cardTypes;
}
@ -534,6 +556,10 @@ public class CardView extends SimpleCardView {
return color;
}
public ObjectColor getFrameColor() {
return frameColor;
}
public List<String> getManaCost() {
return manaCost;
}

View file

@ -28,6 +28,7 @@
package mage.sets.onslaught;
import java.util.UUID;
import mage.ObjectColor;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.common.FetchLandActivatedAbility;
@ -42,6 +43,7 @@ public class BloodstainedMire extends CardImpl {
public BloodstainedMire(UUID ownerId) {
super(ownerId, 313, "Bloodstained Mire", Rarity.RARE, new CardType[]{CardType.LAND}, "");
this.expansionSetCode = "ONS";
this.frameColor = new ObjectColor("RB");
// {tap}, Pay 1 life, Sacrifice Bloodstained Mire: Search your library for a Swamp or Mountain card and put it onto the battlefield. Then shuffle your library.
this.addAbility(new FetchLandActivatedAbility(new String[]{"Swamp", "Mountain"}));

View file

@ -28,6 +28,7 @@
package mage.sets.onslaught;
import java.util.UUID;
import mage.ObjectColor;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.common.FetchLandActivatedAbility;
@ -42,6 +43,7 @@ public class FloodedStrand extends CardImpl {
public FloodedStrand(UUID ownerId) {
super(ownerId, 316, "Flooded Strand", Rarity.RARE, new CardType[]{CardType.LAND}, "");
this.expansionSetCode = "ONS";
this.frameColor = new ObjectColor("UW");
// {tap}, Pay 1 life, Sacrifice Flooded Strand: Search your library for a Plains or Island card and put it onto the battlefield. Then shuffle your library.
this.addAbility(new FetchLandActivatedAbility(new String[]{"Plains", "Island"}));

View file

@ -28,6 +28,7 @@
package mage.sets.onslaught;
import java.util.UUID;
import mage.ObjectColor;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.common.FetchLandActivatedAbility;
@ -42,6 +43,7 @@ public class PollutedDelta extends CardImpl {
public PollutedDelta(UUID ownerId) {
super(ownerId, 321, "Polluted Delta", Rarity.RARE, new CardType[]{CardType.LAND}, "");
this.expansionSetCode = "ONS";
this.frameColor = new ObjectColor("UB");
// {tap}, Pay 1 life, Sacrifice Polluted Delta: Search your library for an Island or Swamp card and put it onto the battlefield. Then shuffle your library.
this.addAbility(new FetchLandActivatedAbility(new String[]{"Island", "Swamp"}));

View file

@ -28,6 +28,7 @@
package mage.sets.onslaught;
import java.util.UUID;
import mage.ObjectColor;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.common.FetchLandActivatedAbility;
@ -42,6 +43,7 @@ public class WindsweptHeath extends CardImpl {
public WindsweptHeath(UUID ownerId) {
super(ownerId, 328, "Windswept Heath", Rarity.RARE, new CardType[]{CardType.LAND}, "");
this.expansionSetCode = "ONS";
this.frameColor = new ObjectColor("GW");
// {tap}, Pay 1 life, Sacrifice Windswept Heath: Search your library for a Forest or Plains card and put it onto the battlefield. Then shuffle your library.
this.addAbility(new FetchLandActivatedAbility(new String[]{"Forest", "Plains"}));

View file

@ -28,6 +28,7 @@
package mage.sets.onslaught;
import java.util.UUID;
import mage.ObjectColor;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.common.FetchLandActivatedAbility;
@ -42,6 +43,7 @@ public class WoodedFoothills extends CardImpl {
public WoodedFoothills(UUID ownerId) {
super(ownerId, 330, "Wooded Foothills", Rarity.RARE, new CardType[]{CardType.LAND}, "");
this.expansionSetCode = "ONS";
this.frameColor = new ObjectColor("RG");
// {tap}, Pay 1 life, Sacrifice Wooded Foothills: Search your library for a Mountain or Forest card and put it onto the battlefield. Then shuffle your library.
this.addAbility(new FetchLandActivatedAbility(new String[]{"Mountain", "Forest"}));

View file

@ -29,6 +29,7 @@
package mage.sets.zendikar;
import java.util.UUID;
import mage.ObjectColor;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.common.FetchLandActivatedAbility;
@ -43,6 +44,7 @@ public class AridMesa extends CardImpl {
public AridMesa(UUID ownerId) {
super(ownerId, 211, "Arid Mesa", Rarity.RARE, new CardType[]{CardType.LAND}, null);
this.expansionSetCode = "ZEN";
this.frameColor = new ObjectColor("WR");
this.addAbility(new FetchLandActivatedAbility(new String[] {"Mountain", "Plains"}));
}

View file

@ -29,6 +29,7 @@
package mage.sets.zendikar;
import java.util.UUID;
import mage.ObjectColor;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.common.FetchLandActivatedAbility;
@ -43,6 +44,7 @@ public class MarshFlats extends CardImpl {
public MarshFlats(UUID ownerId) {
super(ownerId, 219, "Marsh Flats", Rarity.RARE, new CardType[]{CardType.LAND}, null);
this.expansionSetCode = "ZEN";
this.frameColor = new ObjectColor("WB");
this.addAbility(new FetchLandActivatedAbility(new String[] {"Swamp", "Plains"}));
}

View file

@ -29,6 +29,7 @@
package mage.sets.zendikar;
import java.util.UUID;
import mage.ObjectColor;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.common.FetchLandActivatedAbility;
@ -43,6 +44,7 @@ public class MistyRainforest extends CardImpl {
public MistyRainforest(UUID ownerId) {
super(ownerId, 220, "Misty Rainforest", Rarity.RARE, new CardType[]{CardType.LAND}, null);
this.expansionSetCode = "ZEN";
this.frameColor = new ObjectColor("UG");
this.addAbility(new FetchLandActivatedAbility(new String[] {"Forest", "Island"}));
}

View file

@ -29,6 +29,7 @@
package mage.sets.zendikar;
import java.util.UUID;
import mage.ObjectColor;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.common.FetchLandActivatedAbility;
@ -43,6 +44,7 @@ public class ScaldingTarn extends CardImpl {
public ScaldingTarn(UUID ownerId) {
super(ownerId, 223, "Scalding Tarn", Rarity.RARE, new CardType[]{CardType.LAND}, null);
this.expansionSetCode = "ZEN";
this.frameColor = new ObjectColor("UR");
this.addAbility(new FetchLandActivatedAbility(new String[] {"Island", "Mountain"}));
}

View file

@ -29,6 +29,7 @@
package mage.sets.zendikar;
import java.util.UUID;
import mage.ObjectColor;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.common.FetchLandActivatedAbility;
@ -43,6 +44,7 @@ public class VerdantCatacombs extends CardImpl {
public VerdantCatacombs(UUID ownerId) {
super(ownerId, 229, "Verdant Catacombs", Rarity.RARE, new CardType[]{CardType.LAND}, null);
this.expansionSetCode = "ZEN";
this.frameColor = new ObjectColor("BG");
this.addAbility(new FetchLandActivatedAbility(new String[] {"Forest", "Swamp"}));
}

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,8 @@ public interface MageObject extends MageItem, Serializable {
ObjectColor getColor(Game game);
ObjectColor getFrameColor(Game game);
ManaCosts<ManaCost> getManaCost();
int getConvertedManaCost();
@ -44,6 +46,10 @@ public interface MageObject extends MageItem, Serializable {
MageInt getToughness();
int getStartingLoyalty();
void adjustCosts(Ability ability, Game game);
void adjustTargets(Ability ability, Game game);

View file

@ -33,6 +33,7 @@ import java.util.UUID;
import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl;
import mage.abilities.Ability;
import mage.abilities.common.PlanswalkerEntersWithLoyalityCountersAbility;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.ManaCostsImpl;
@ -49,6 +50,7 @@ public abstract class MageObjectImpl implements MageObject {
protected String name;
protected ManaCosts<ManaCost> manaCost;
protected ObjectColor color;
protected ObjectColor frameColor;
protected List<CardType> cardType = new ArrayList<>();
protected List<String> subtype = new ArrayList<>();
protected List<String> supertype = new ArrayList<>();
@ -67,6 +69,7 @@ public abstract class MageObjectImpl implements MageObject {
power = new MageInt(0);
toughness = new MageInt(0);
color = new ObjectColor();
frameColor = new ObjectColor();
manaCost = new ManaCostsImpl<>("");
abilities = new AbilitiesImpl<>();
}
@ -77,6 +80,7 @@ public abstract class MageObjectImpl implements MageObject {
manaCost = object.manaCost.copy();
text = object.text;
color = object.color.copy();
frameColor = object.frameColor.copy();
power = object.power.copy();
toughness = object.toughness.copy();
abilities = object.abilities.copy();
@ -155,11 +159,26 @@ public abstract class MageObjectImpl implements MageObject {
return toughness;
}
@Override
public int getStartingLoyalty() {
for (Ability ab: getAbilities()) {
if (ab instanceof PlanswalkerEntersWithLoyalityCountersAbility) {
return ((PlanswalkerEntersWithLoyalityCountersAbility)ab).getStartingLoyalty();
}
}
return 0;
}
@Override
public ObjectColor getColor(Game game) {
return color;
}
@Override
public ObjectColor getFrameColor(Game game) {
return frameColor;
}
@Override
public ManaCosts<ManaCost> getManaCost() {
return manaCost;

View file

@ -82,6 +82,22 @@ public class ObjectColor implements Serializable, Copyable<ObjectColor>, Compara
green = color.green;
}
/**
* Returns a new color which contains all of the colors of this ObjectColor
* in addition to all of the colors of the other ObjectColor.
* @param other The other ObjectColor to union with
* @return A new color which is the union of this and other
*/
public ObjectColor union(ObjectColor other) {
ObjectColor newColor = new ObjectColor();
newColor.white = white | other.white;
newColor.blue = blue | other.blue;
newColor.black = black | other.black;
newColor.red = red | other.red;
newColor.green = green | other.green;
return newColor;
}
public int getColorCount() {
int count = 0;
if (white) {

View file

@ -14,13 +14,21 @@ import mage.counters.CounterType;
*/
public class PlanswalkerEntersWithLoyalityCountersAbility extends EntersBattlefieldAbility {
public PlanswalkerEntersWithLoyalityCountersAbility(int loyality) {
super(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(loyality)));
private final int startingLoyalty;
public PlanswalkerEntersWithLoyalityCountersAbility(int loyalty) {
super(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(loyalty)));
startingLoyalty = loyalty;
setRuleVisible(false);
}
public PlanswalkerEntersWithLoyalityCountersAbility(final PlanswalkerEntersWithLoyalityCountersAbility ability) {
super(ability);
startingLoyalty = ability.startingLoyalty;
}
public int getStartingLoyalty() {
return startingLoyalty;
}
@Override

View file

@ -29,6 +29,7 @@
package mage.cards.basiclands;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.mana.GreenManaAbility;
/**
@ -43,6 +44,7 @@ public abstract class Forest extends BasicLand {
public Forest(UUID ownerId, String cardNumber) {
super(ownerId, cardNumber, "Forest", new GreenManaAbility());
this.frameColor = ObjectColor.GREEN;
}
public Forest(final Forest land) {

View file

@ -29,6 +29,7 @@
package mage.cards.basiclands;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.mana.BlueManaAbility;
/**
@ -43,6 +44,7 @@ public abstract class Island extends BasicLand {
public Island(UUID ownerId, String cardNumber) {
super(ownerId, cardNumber, "Island", new BlueManaAbility());
this.frameColor = ObjectColor.BLUE;
}
public Island(Island land) {

View file

@ -29,6 +29,7 @@
package mage.cards.basiclands;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.mana.RedManaAbility;
/**
@ -43,6 +44,7 @@ public abstract class Mountain extends BasicLand {
public Mountain(UUID ownerId, String cardNumber) {
super(ownerId, cardNumber, "Mountain", new RedManaAbility());
this.frameColor = ObjectColor.RED;
}
public Mountain(Mountain land) {

View file

@ -29,6 +29,7 @@
package mage.cards.basiclands;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.mana.WhiteManaAbility;
/**
@ -43,6 +44,7 @@ public abstract class Plains extends BasicLand {
public Plains(UUID ownerId, String cardNumber) {
super(ownerId, cardNumber, "Plains", new WhiteManaAbility());
this.frameColor = ObjectColor.WHITE;
}
public Plains(Plains land) {

View file

@ -29,6 +29,7 @@
package mage.cards.basiclands;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.mana.BlackManaAbility;
/**
@ -43,6 +44,7 @@ public abstract class Swamp extends BasicLand {
public Swamp(UUID ownerId, String cardNumber) {
super(ownerId, cardNumber, "Swamp", new BlackManaAbility());
this.frameColor = ObjectColor.BLACK;
}
public Swamp(Swamp land) {

View file

@ -7,11 +7,18 @@ import mage.abilities.costs.mana.ManaCostsImpl;
import mage.cards.CardImpl;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.CardType;
import org.apache.log4j.Logger;
/**
* @author North
*/
public class MockCard extends CardImpl {
// Needs to be here, as it is normally calculated from the
// PlaneswalkerEntersWithLoyaltyAbility of the card... but the MockCard
// only has MockAbilities.
private int startingLoyalty;
public MockCard(CardInfo card) {
super(null, card.getName());
this.cardNumber = card.getCardNumber();
@ -28,6 +35,9 @@ public class MockCard extends CardImpl {
this.manaCost = new ManaCostsImpl(join(card.getManaCosts()));
this.color = card.getColor();
this.frameColor = card.getFrameColor();
this.splitCard = card.isSplitCard();
this.flipCard = card.isFlipCard();
@ -37,8 +47,20 @@ public class MockCard extends CardImpl {
this.secondSideCard = new MockCard(CardRepository.instance.findCard(card.getSecondSideName()));
}
this.flipCardName = card.getFlipCardName();
if (this.cardType.contains(CardType.PLANESWALKER)) {
String startingLoyaltyString = card.getStartingLoyalty();
if (startingLoyaltyString.isEmpty()) {
Logger.getLogger(MockCard.class).warn("Planeswalker `" + this.name + "` has empty starting loyalty.");
} else {
try {
this.startingLoyalty = Integer.parseInt(startingLoyaltyString);
} catch (NumberFormatException e) {
Logger.getLogger(MockCard.class).warn("Planeswalker `" + this.name + "` starting loyalty in bad format: `" + startingLoyaltyString + "`.");
}
}
}
this.flipCardName = card.getFlipCardName();
for(String ruleText: card.getRules()) {
this.addAbility(textAbilityFromString(ruleText));
}
@ -48,6 +70,11 @@ public class MockCard extends CardImpl {
super(card);
}
@Override
public int getStartingLoyalty() {
return startingLoyalty;
}
@Override
public MockCard copy() {
return new MockCard(this);

View file

@ -39,7 +39,10 @@ import java.util.List;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.PlanswalkerEntersWithLoyalityCountersAbility;
import mage.abilities.effects.PlaneswalkerRedirectionEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.mock.MockCard;
@ -70,6 +73,8 @@ public class CardInfo {
@DatabaseField
protected String toughness;
@DatabaseField
protected String startingLoyalty;
@DatabaseField
protected int convertedManaCost;
@DatabaseField(dataType = DataType.ENUM_STRING)
protected Rarity rarity;
@ -94,6 +99,8 @@ public class CardInfo {
@DatabaseField
protected boolean white;
@DatabaseField
protected String frameColor;
@DatabaseField
protected boolean splitCard;
@DatabaseField
protected boolean splitCardHalf;
@ -132,6 +139,7 @@ public class CardInfo {
this.secondSideName = secondSide.getName();
}
this.frameColor = card.getFrameColor(null).toString();
this.blue = card.getColor(null).isBlue();
this.black = card.getColor(null).isBlack();
this.green = card.getColor(null).isGreen();
@ -144,13 +152,13 @@ public class CardInfo {
this.setManaCosts(card.getManaCost().getSymbols());
int length = 0;
for (String rule :card.getRules()) {
for (String rule: card.getRules()) {
length += rule.length();
}
if (length > MAX_RULE_LENGTH) {
length = 0;
ArrayList<String> shortRules = new ArrayList<>();
for (String rule :card.getRules()) {
for (String rule: card.getRules()) {
if (length + rule.length() + 3 <= MAX_RULE_LENGTH) {
shortRules.add(rule);
length += rule.length() + 3;
@ -173,6 +181,21 @@ public class CardInfo {
this.splitCardHalf = true;
}
}
// Starting loyalty
if (card.getCardType().contains(CardType.PLANESWALKER)) {
for (Ability ab: card.getAbilities()) {
if (ab instanceof PlanswalkerEntersWithLoyalityCountersAbility) {
this.startingLoyalty = "" + ((PlanswalkerEntersWithLoyalityCountersAbility) ab).getStartingLoyalty();
}
}
if (this.startingLoyalty == null) {
Logger.getLogger(CardInfo.class).warn("Planeswalker `" + card.getName() + "` missing starting loyalty");
this.startingLoyalty = "";
}
} else {
this.startingLoyalty = "";
}
}
public Card getCard() {
@ -201,6 +224,10 @@ public class CardInfo {
return color;
}
public ObjectColor getFrameColor() {
return new ObjectColor(frameColor);
}
private String joinList(List<String> items) {
StringBuilder sb = new StringBuilder();
for (Object item : items) {
@ -287,6 +314,10 @@ public class CardInfo {
return toughness;
}
public String getStartingLoyalty() {
return startingLoyalty;
}
public String getSetCode() {
return setCode;
}

View file

@ -145,6 +145,11 @@ public class Commander implements CommandObject {
return card.getColor(game);
}
@Override
public ObjectColor getFrameColor(Game game) {
return card.getFrameColor(game);
}
@Override
public ManaCosts<ManaCost> getManaCost() {
return card.getManaCost();
@ -165,6 +170,11 @@ public class Commander implements CommandObject {
return card.getToughness();
}
@Override
public int getStartingLoyalty() {
return card.getStartingLoyalty();
}
@Override
public void adjustCosts(Ability ability, Game game) {
}

View file

@ -154,6 +154,11 @@ public class Emblem implements CommandObject {
return emptyColor;
}
@Override
public ObjectColor getFrameColor(Game game) {
return emptyColor;
}
@Override
public ManaCosts<ManaCost> getManaCost() {
return emptyCost;
@ -174,6 +179,11 @@ public class Emblem implements CommandObject {
return MageInt.EmptyMageInt;
}
@Override
public int getStartingLoyalty() {
return 0;
}
@Override
public void adjustCosts(Ability ability, Game game) {
}

View file

@ -75,6 +75,7 @@ public class Spell extends StackObjImpl implements Card {
private final Card card;
private final ObjectColor color;
private final ObjectColor frameColor;
private final SpellAbility ability;
private final Zone fromZone;
private final UUID id;
@ -87,6 +88,7 @@ public class Spell extends StackObjImpl implements Card {
public Spell(Card card, SpellAbility ability, UUID controllerId, Zone fromZone) {
this.card = card;
this.color = card.getColor(null).copy();
this.frameColor = card.getFrameColor(null).copy();
id = ability.getId();
this.ability = ability;
this.ability.setControllerId(controllerId);
@ -127,6 +129,7 @@ public class Spell extends StackObjImpl implements Card {
this.copiedSpell = spell.copiedSpell;
this.faceDown = spell.faceDown;
this.color = spell.color.copy();
this.frameColor = spell.color.copy();
}
public boolean activate(Game game, boolean noMana) {
@ -483,6 +486,11 @@ public class Spell extends StackObjImpl implements Card {
return color;
}
@Override
public ObjectColor getFrameColor(Game game) {
return frameColor;
}
@Override
public ManaCosts<ManaCost> getManaCost() {
return card.getManaCost();
@ -519,6 +527,11 @@ public class Spell extends StackObjImpl implements Card {
return card.getToughness();
}
@Override
public int getStartingLoyalty() {
return card.getStartingLoyalty();
}
@Override
public UUID getId() {
return id;

View file

@ -193,6 +193,11 @@ public class StackAbility extends StackObjImpl implements Ability {
return emptyColor;
}
@Override
public ObjectColor getFrameColor(Game game) {
return ability.getSourceObject(game).getFrameColor(game);
}
@Override
public ManaCosts<ManaCost> getManaCost() {
return emptyCost;
@ -208,6 +213,11 @@ public class StackAbility extends StackObjImpl implements Ability {
return MageInt.EmptyMageInt;
}
@Override
public int getStartingLoyalty() {
return 0;
}
@Override
public Zone getZone() {
return this.ability.getZone();