mirror of
https://github.com/correl/mage.git
synced 2024-12-25 03:00:15 +00:00
Merge pull request #40 from magefree/master
Merge https://github.com/magefree/mage
This commit is contained in:
commit
adec5cf88b
27 changed files with 554 additions and 331 deletions
|
@ -753,7 +753,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
currentConnection.setPort(port);
|
||||
String allMAC = "";
|
||||
try {
|
||||
allMAC = currentConnection.getMAC();
|
||||
allMAC = Connection.getMAC();
|
||||
} catch (SocketException ex) {
|
||||
}
|
||||
currentConnection.setUserIdStr(System.getProperty("user.name") + ":" + System.getProperty("os.name") + ":" + MagePreferences.getUserNames() + ":" + allMAC);
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
*/
|
||||
package mage.client.deckeditor.collection.viewer;
|
||||
|
||||
import java.awt.Color;
|
||||
|
@ -48,7 +48,6 @@ import mage.client.util.gui.FastSearchUtil;
|
|||
import mage.client.util.sets.ConstructedFormats;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* Pane with big card and mage book.
|
||||
*
|
||||
|
@ -75,7 +74,7 @@ public final class CollectionViewerPanel extends JPanel {
|
|||
this.hidePopup();
|
||||
this.bigCard = null;
|
||||
}
|
||||
|
||||
|
||||
public void initComponents() {
|
||||
buttonsPanel = new javax.swing.JPanel();
|
||||
buttonsPanel.setOpaque(false);
|
||||
|
@ -117,6 +116,7 @@ public final class CollectionViewerPanel extends JPanel {
|
|||
btnSetFastSearch.setPreferredSize(new java.awt.Dimension(32, 32));
|
||||
btnSetFastSearch.setMaximumSize(new java.awt.Dimension(32, 32));
|
||||
btnSetFastSearch.addActionListener(new java.awt.event.ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
FastSearchUtil.showFastSearchForStringComboBox(formats, FastSearchUtil.DEFAULT_EXPANSION_SEARCH_MESSAGE);
|
||||
}
|
||||
|
@ -169,20 +169,21 @@ public final class CollectionViewerPanel extends JPanel {
|
|||
next.addActionListener(e -> mageBook.next());
|
||||
buttonPanel.add(next);
|
||||
|
||||
JLabel label4 = new JLabel("Show cards or tokens:");
|
||||
label3.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
label3.setForeground(Color.white);
|
||||
buttonsPanel.add(label4);
|
||||
JLabel labelCardTokenSwitch = new JLabel("Show cards or tokens:");
|
||||
labelCardTokenSwitch.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
labelCardTokenSwitch.setForeground(Color.white);
|
||||
buttonsPanel.add(labelCardTokenSwitch);
|
||||
|
||||
JCheckBox cardsOrTokens = new JCheckBox("Display Cards");
|
||||
cardsOrTokens.setSelected(true);
|
||||
cardsOrTokens.setForeground(Color.white);
|
||||
cardsOrTokens.setToolTipText("Select to show Cards or Tokens(and emblems) for the chosen set");
|
||||
cardsOrTokens.addActionListener(e -> mageBook.cardsOrTokens(cardsOrTokens.isSelected()));
|
||||
buttonsPanel.add(cardsOrTokens);
|
||||
|
||||
formats.addActionListener(e -> {
|
||||
if (mageBook != null) {
|
||||
String format = (String)formats.getSelectedItem();
|
||||
String format = (String) formats.getSelectedItem();
|
||||
MageFrame.getPreferences().put(CollectionViewerPanel.FORMAT_CONFIG_KEY, format);
|
||||
mageBook.updateDispayedSets(format);
|
||||
}
|
||||
|
@ -233,11 +234,13 @@ public final class CollectionViewerPanel extends JPanel {
|
|||
c = c.getParent();
|
||||
}
|
||||
if (c != null) {
|
||||
((CollectionViewerPane)c).removeFrame();
|
||||
((CollectionViewerPane) c).removeFrame();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class MageBookContainer extends JPanel {
|
||||
|
||||
public MageBookContainer() {
|
||||
super();
|
||||
initComponents();
|
||||
|
@ -246,7 +249,7 @@ public final class CollectionViewerPanel extends JPanel {
|
|||
public void initComponents() {
|
||||
jPanel = new JPanel();
|
||||
jScrollPane1 = new JScrollPane(jPanel);
|
||||
jScrollPane1.getViewport().setBackground(new Color(0,0,0,0));
|
||||
jScrollPane1.getViewport().setBackground(new Color(0, 0, 0, 0));
|
||||
|
||||
jPanel.setLayout(new GridBagLayout()); // centers mage book
|
||||
jPanel.setBackground(new Color(0, 0, 0, 0));
|
||||
|
|
|
@ -27,6 +27,19 @@
|
|||
*/
|
||||
package mage.client.deckeditor.collection.viewer;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import static java.lang.Math.min;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import mage.cards.*;
|
||||
import mage.cards.repository.CardCriteria;
|
||||
import mage.cards.repository.CardInfo;
|
||||
|
@ -42,29 +55,15 @@ import mage.client.util.sets.ConstructedFormats;
|
|||
import mage.components.ImagePanel;
|
||||
import mage.components.ImagePanelStyle;
|
||||
import mage.constants.Rarity;
|
||||
import mage.view.CardView;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.card.arcane.ManaSymbols;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import mage.game.command.Emblem;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
import mage.game.permanent.token.Token;
|
||||
import mage.view.CardView;
|
||||
import mage.view.EmblemView;
|
||||
import mage.view.PermanentView;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.card.arcane.ManaSymbols;
|
||||
import org.mage.plugins.card.images.CardDownloadData;
|
||||
|
||||
import static java.lang.Math.min;
|
||||
import static org.mage.plugins.card.images.DownloadPictures.getTokenCardUrls;
|
||||
|
||||
/**
|
||||
|
@ -144,7 +143,6 @@ public class MageBook extends JComponent {
|
|||
|
||||
int captionHeight = Math.max(30, pageLeft.getHeight()); // caption size = next-prev images
|
||||
|
||||
|
||||
// Top Panel (left page + (caption / stats) + right page
|
||||
jPanelTop = new JPanel();
|
||||
jPanelTop.setLayout(new BorderLayout());
|
||||
|
@ -290,11 +288,9 @@ public class MageBook extends JComponent {
|
|||
|
||||
public int showTokens() {
|
||||
jLayeredPane.removeAll();
|
||||
|
||||
List<Token> tokens = getTokens(currentPage, currentSet);
|
||||
int size = tokens.size();
|
||||
|
||||
if (tokens != null && tokens.size() > 0) {
|
||||
int size = tokens.size();
|
||||
Rectangle rectangle = new Rectangle();
|
||||
rectangle.translate(OFFSET_X, OFFSET_Y);
|
||||
for (int i = 0; i < min(conf.CARDS_PER_PAGE / 2, size); i++) {
|
||||
|
@ -316,35 +312,34 @@ public class MageBook extends JComponent {
|
|||
|
||||
jLayeredPane.repaint();
|
||||
}
|
||||
|
||||
|
||||
return tokens.size();
|
||||
}
|
||||
|
||||
public void showEmblems(int numTokens) {
|
||||
List<Emblem> emblems = getEmblems(currentPage, currentSet, numTokens);
|
||||
int size = emblems.size();
|
||||
System.out.println ("Size of origins in " + currentSet + " = " + emblems.size());
|
||||
|
||||
// System.out.println ("Size of origins in " + currentSet + " = " + emblems.size());
|
||||
if (emblems != null && emblems.size() > 0) {
|
||||
int size = emblems.size();
|
||||
Rectangle rectangle = new Rectangle();
|
||||
rectangle.translate(OFFSET_X, OFFSET_Y);
|
||||
// calculate the x offset of the second (right) page
|
||||
int second_page_x = (conf.WIDTH - 2 * LEFT_RIGHT_PAGES_WIDTH)
|
||||
- (cardDimensions.frameWidth + CardPosition.GAP_X) * conf.CARD_COLUMNS + CardPosition.GAP_X - OFFSET_X;
|
||||
|
||||
// Already have numTokens tokens presented. Appending the emblems to the end of these.
|
||||
// Already have numTokens tokens presented. Appending the emblems to the end of these.
|
||||
numTokens = numTokens % conf.CARDS_PER_PAGE;
|
||||
if (numTokens < conf.CARDS_PER_PAGE / 2) {
|
||||
for (int z = 0; z < numTokens && z < conf.CARDS_PER_PAGE / 2; z++) {
|
||||
rectangle = CardPosition.translatePosition(z, rectangle, conf);
|
||||
}
|
||||
} else {
|
||||
rectangle.setLocation(second_page_x, OFFSET_Y);
|
||||
rectangle.setLocation(second_page_x, OFFSET_Y);
|
||||
for (int z = 0; z < numTokens - conf.CARDS_PER_PAGE / 2; z++) {
|
||||
rectangle = CardPosition.translatePosition(z, rectangle, conf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int lastI = 0;
|
||||
for (int i = 0; i < size && i + numTokens < conf.CARDS_PER_PAGE / 2; i++) {
|
||||
Emblem emblem = emblems.get(i);
|
||||
|
@ -388,8 +383,7 @@ public class MageBook extends JComponent {
|
|||
int dx = implemented ? 15 : 5;
|
||||
label.setBounds(rectangle.x + dx, rectangle.y + cardDimensions.frameHeight + 7, 110, 30);
|
||||
jLayeredPane.add(label);
|
||||
*/
|
||||
|
||||
*/
|
||||
// card number label
|
||||
JLabel cardNumber = new JLabel();
|
||||
int dy = -5; // image panel have empty space in bottom (bug?), need to move label up
|
||||
|
@ -436,26 +430,25 @@ public class MageBook extends JComponent {
|
|||
return cards.subList(start, end);
|
||||
}
|
||||
|
||||
private void updateCardStats(String setCode, boolean isCardsShow){
|
||||
private void updateCardStats(String setCode, boolean isCardsShow) {
|
||||
// sets do not have total cards number, it's a workaround
|
||||
|
||||
ExpansionSet set = Sets.findSet(setCode);
|
||||
if (set != null){
|
||||
if (set != null) {
|
||||
setCaption.setText(set.getCode() + " - " + set.getName());
|
||||
}else{
|
||||
} else {
|
||||
setCaption.setText("ERROR");
|
||||
setInfo.setText("ERROR");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCardsShow){
|
||||
if (!isCardsShow) {
|
||||
// tokens or emblems, stats not need
|
||||
setInfo.setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
// cards stats
|
||||
|
||||
int startNumber = 9999;
|
||||
int endNumber = 0;
|
||||
|
||||
|
@ -463,11 +456,11 @@ public class MageBook extends JComponent {
|
|||
|
||||
// first run for numbers list
|
||||
LinkedList<Integer> haveNumbers = new LinkedList<>();
|
||||
for (ExpansionSet.SetCardInfo card: cards){
|
||||
for (ExpansionSet.SetCardInfo card : cards) {
|
||||
int cardNumber = card.getCardNumberAsInt();
|
||||
|
||||
// skip xmage special numbers for cards (TODO: replace full art cards numbers from 180+20 to 180b, 180c and vice versa like scryfall)
|
||||
if(cardNumber > 500){
|
||||
if (cardNumber > 500) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -479,19 +472,19 @@ public class MageBook extends JComponent {
|
|||
// second run for empty numbers
|
||||
int countHave = haveNumbers.size();
|
||||
int countNotHave = 0;
|
||||
if (cards.size() > 0){
|
||||
for(int i = startNumber; i <= endNumber; i++){
|
||||
if(!haveNumbers.contains(i)){
|
||||
countNotHave++;
|
||||
}
|
||||
}
|
||||
if (cards.size() > 0) {
|
||||
for (int i = startNumber; i <= endNumber; i++) {
|
||||
if (!haveNumbers.contains(i)) {
|
||||
countNotHave++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// result
|
||||
setInfo.setText(String.format("Have %d cards of %d", countHave, countHave + countNotHave));
|
||||
if (countNotHave > 0) {
|
||||
setInfo.setForeground(new Color(150, 0, 0));
|
||||
}else{
|
||||
} else {
|
||||
setInfo.setForeground(jLayeredPane.getForeground());
|
||||
}
|
||||
}
|
||||
|
@ -520,21 +513,10 @@ public class MageBook extends JComponent {
|
|||
if (newToken != null && newToken instanceof mage.game.permanent.token.Token) {
|
||||
((Token) newToken).setExpansionSetCodeForImage(set);
|
||||
((Token) newToken).setOriginalExpansionSetCode(set);
|
||||
((Token) newToken).setTokenType(token.getType());
|
||||
tokens.add((Token) newToken);
|
||||
}
|
||||
} catch (ClassNotFoundException ex) {
|
||||
// Swallow exception
|
||||
} catch (NoSuchMethodException ex) {
|
||||
// Swallow exception
|
||||
} catch (SecurityException ex) {
|
||||
// Swallow exception
|
||||
} catch (InstantiationException ex) {
|
||||
// Swallow exception
|
||||
} catch (IllegalAccessException ex) {
|
||||
// Swallow exception
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Swallow exception
|
||||
} catch (InvocationTargetException ex) {
|
||||
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
// Swallow exception
|
||||
}
|
||||
}
|
||||
|
@ -571,7 +553,7 @@ public class MageBook extends JComponent {
|
|||
Object newEmblem = cons.newInstance();
|
||||
if (newEmblem != null && newEmblem instanceof mage.game.command.Emblem) {
|
||||
((Emblem) newEmblem).setExpansionSetCodeForImage(set);
|
||||
|
||||
|
||||
emblems.add((Emblem) newEmblem);
|
||||
}
|
||||
} catch (ClassNotFoundException ex) {
|
||||
|
@ -593,12 +575,12 @@ public class MageBook extends JComponent {
|
|||
}
|
||||
int start = 0;
|
||||
int end = emblems.size();
|
||||
|
||||
if ((page + 1) * conf.CARDS_PER_PAGE < numTokens + emblems.size()) {
|
||||
|
||||
if ((page + 1) * conf.CARDS_PER_PAGE < numTokens + emblems.size()) {
|
||||
end = (page + 1) * conf.CARDS_PER_PAGE - numTokens;
|
||||
pageRight.setVisible(true);
|
||||
}
|
||||
|
||||
|
||||
if (emblems.size() > conf.CARDS_PER_PAGE) {
|
||||
pageLeft.setVisible(true);
|
||||
pageRight.setVisible(true);
|
||||
|
|
|
@ -31,7 +31,9 @@ import java.io.BufferedReader;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
|
@ -39,9 +41,11 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import mage.constants.SubType;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.plugins.card.images.CardDownloadData;
|
||||
import org.mage.plugins.card.images.DownloadPictures;
|
||||
import org.mage.plugins.card.utils.CardImageUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -83,35 +87,6 @@ public enum TokensMtgImageSource implements CardImageSource {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static final String[] EMBLEMS = {
|
||||
"Ajani",
|
||||
"Arlinn",
|
||||
"Chandra",
|
||||
"Dack",
|
||||
"Daretti",
|
||||
"Dovin",
|
||||
"Domri",
|
||||
"Elspeth",
|
||||
"Garruk",
|
||||
"Gideon",
|
||||
"Huatli",
|
||||
"Jace",
|
||||
"Kiora",
|
||||
"Koth",
|
||||
"Liliana",
|
||||
"Narset",
|
||||
"Nixilis",
|
||||
"Sarkhan",
|
||||
"Sorin",
|
||||
"Tamiyo",
|
||||
"Teferi",
|
||||
"Venser",
|
||||
// Custom Emblems
|
||||
"Yoda",
|
||||
"Obi-Wan Kenobi",
|
||||
"Aurra Sing"
|
||||
};
|
||||
|
||||
private static final Map<String, String> SET_NAMES_REPLACEMENT = new HashMap<String, String>() {
|
||||
{
|
||||
put("con", "CFX");
|
||||
|
@ -119,6 +94,16 @@ public enum TokensMtgImageSource implements CardImageSource {
|
|||
}
|
||||
};
|
||||
|
||||
private String getEmblemName(String originalName) {
|
||||
|
||||
for (SubType subType : SubType.getPlaneswalkerTypes(true)) {
|
||||
if (originalName.toLowerCase().contains(subType.toString().toLowerCase())) {
|
||||
return subType.getDescription() + " Emblem";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTokenUrl(CardDownloadData card) throws IOException {
|
||||
String name = card.getName();
|
||||
|
@ -127,12 +112,7 @@ public enum TokensMtgImageSource implements CardImageSource {
|
|||
|
||||
// handle emblems
|
||||
if (name.toLowerCase().contains("emblem")) {
|
||||
for (String emblem : EMBLEMS) {
|
||||
if (name.toLowerCase().contains(emblem.toLowerCase())) {
|
||||
name = emblem + " Emblem";
|
||||
break;
|
||||
}
|
||||
}
|
||||
name = getEmblemName(name);
|
||||
}
|
||||
|
||||
// we should replace some set names
|
||||
|
@ -152,20 +132,16 @@ public enum TokensMtgImageSource implements CardImageSource {
|
|||
String key = set + "/" + name;
|
||||
List<TokenData> list = tokensData.get(key);
|
||||
if (list == null) {
|
||||
logger.info("Could not find data for token " + name + ", set " + set + ".");
|
||||
logger.warn("Could not find data for token " + name + ", set " + set + ".");
|
||||
return null;
|
||||
}
|
||||
|
||||
TokenData tokenData;
|
||||
if (type == 0) {
|
||||
if (list.size() > 1) {
|
||||
logger.info("Multiple images were found for token " + name + ", set " + set + '.');
|
||||
}
|
||||
logger.info("Token found: " + name + ", set " + set + '.');
|
||||
tokenData = list.get(0);
|
||||
} else {
|
||||
if (type > list.size()) {
|
||||
logger.warn("Not enough images for token with type " + type + ", name " + name + ", set " + set + '.');
|
||||
logger.warn("Not enough images variants for token with type number " + type + ", name " + name + ", set " + set + '.');
|
||||
return null;
|
||||
}
|
||||
tokenData = list.get(card.getType() - 1);
|
||||
|
@ -177,6 +153,57 @@ public enum TokensMtgImageSource implements CardImageSource {
|
|||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalImages() {
|
||||
return getTokenImages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTokenImages() {
|
||||
try {
|
||||
getTokensData();
|
||||
} catch (IOException ex) {
|
||||
logger.error(getSourceName() + ": Loading available data failed. " + ex.getMessage());
|
||||
}
|
||||
return tokensData.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTokenSource() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doPause(String httpImageUrl) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<String> getSupportedSets() {
|
||||
ArrayList<String> supportedSetsCopy = new ArrayList<>();
|
||||
supportedSetsCopy.addAll(supportedSets);
|
||||
return supportedSetsCopy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImageProvided(String setCode, String cardName) {
|
||||
String searchName = cardName;
|
||||
if (cardName.toLowerCase().contains("emblem")) {
|
||||
searchName = getEmblemName(cardName);
|
||||
}
|
||||
try {
|
||||
getTokensData();
|
||||
} catch (IOException ex) {
|
||||
java.util.logging.Logger.getLogger(TokensMtgImageSource.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
String key = setCode + "/" + searchName;
|
||||
return (tokensData.containsKey(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSetSupportedComplete(String setCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private HashMap<String, ArrayList<TokenData>> getTokensData() throws IOException {
|
||||
synchronized (tokensDataSync) {
|
||||
if (tokensData == null) {
|
||||
|
@ -193,7 +220,7 @@ public enum TokensMtgImageSource implements CardImageSource {
|
|||
list = new ArrayList<>();
|
||||
tokensData.put(key, list);
|
||||
supportedSets.add(tokenData.getExpansionSetCode());
|
||||
logger.info("Added key: " + key);
|
||||
logger.debug("Added key: " + key);
|
||||
}
|
||||
list.add(tokenData);
|
||||
}
|
||||
|
@ -201,12 +228,16 @@ public enum TokensMtgImageSource implements CardImageSource {
|
|||
logger.warn("Failed to get tokens description from resource file tokens-mtg-onl-list.csv", exception);
|
||||
}
|
||||
|
||||
String urlString = "http://tokens.mtg.onl/data/SetsWithTokens.csv";
|
||||
Proxy proxy = CardImageUtils.getProxyFromPreferences();
|
||||
URLConnection conn = proxy == null ? new URL(urlString).openConnection() : new URL(urlString).openConnection(proxy);
|
||||
|
||||
// description on site may contain new information
|
||||
// try to add it
|
||||
URL url = new URL("http://tokens.mtg.onl/data/SetsWithTokens.csv");
|
||||
try (InputStream inputStream = url.openStream()) {
|
||||
try (InputStream inputStream = conn.getURL().openStream()) {
|
||||
List<TokenData> siteTokensData = parseTokensData(inputStream);
|
||||
for (TokenData siteData : siteTokensData) {
|
||||
// logger.info("TOK: " + siteData.getExpansionSetCode() + "/" + siteData.getName());
|
||||
String key = siteData.getExpansionSetCode() + "/" + siteData.getName();
|
||||
supportedSets.add(siteData.getExpansionSetCode());
|
||||
ArrayList<TokenData> list = tokensData.get(key);
|
||||
|
@ -267,7 +298,7 @@ public enum TokensMtgImageSource implements CardImageSource {
|
|||
}
|
||||
String[] split = line.split(",");
|
||||
// replace special comma for cards like 'Ashaya‚ the Awoken World'
|
||||
String name = split[0].replace('‚', ',');
|
||||
String name = split[0].replace('‚', ',').replace("‚", ",");
|
||||
String number = split[1];
|
||||
TokenData token = new TokenData(name, number, set);
|
||||
newTokensData.add(token);
|
||||
|
@ -305,51 +336,4 @@ public enum TokensMtgImageSource implements CardImageSource {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalImages() {
|
||||
return getTokenImages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTokenImages() {
|
||||
try {
|
||||
getTokensData();
|
||||
} catch (IOException ex) {
|
||||
logger.error(getSourceName() + ": Loading available data failed. " + ex.getMessage());
|
||||
}
|
||||
return tokensData.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTokenSource() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doPause(String httpImageUrl) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<String> getSupportedSets() {
|
||||
ArrayList<String> supportedSetsCopy = new ArrayList<>();
|
||||
supportedSetsCopy.addAll(supportedSets);
|
||||
return supportedSetsCopy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImageProvided(String setCode, String cardName) {
|
||||
try {
|
||||
getTokensData();
|
||||
} catch (IOException ex) {
|
||||
java.util.logging.Logger.getLogger(TokensMtgImageSource.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
String key = setCode + "/" + cardName;
|
||||
return (tokensData.containsKey(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSetSupportedComplete(String setCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -352,7 +352,7 @@ public enum WizardCardsImageSource implements CardImageSource {
|
|||
setsAliases.put("EVG", "Duel Decks: Elves vs. Goblins");
|
||||
setsAliases.put("EXO", "Exodus");
|
||||
setsAliases.put("FEM", "Fallen Empires");
|
||||
setsAliases.put("FNMP", "Friday Night Magic");
|
||||
// setsAliases.put("FNMP", "Friday Night Magic");
|
||||
setsAliases.put("FRF", "Fate Reforged");
|
||||
setsAliases.put("FUT", "Future Sight");
|
||||
setsAliases.put("GPT", "Guildpact");
|
||||
|
|
|
@ -2,24 +2,17 @@ package org.mage.plugins.card.images;
|
|||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.file.AccessDeniedException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.stream.FileImageOutputStream;
|
||||
import javax.swing.*;
|
||||
import mage.cards.ExpansionSet;
|
||||
import mage.cards.Sets;
|
||||
|
@ -38,13 +31,11 @@ import org.apache.log4j.Logger;
|
|||
import org.mage.plugins.card.dl.sources.*;
|
||||
import org.mage.plugins.card.properties.SettingsManager;
|
||||
import org.mage.plugins.card.utils.CardImageUtils;
|
||||
|
||||
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
|
||||
|
||||
public class DownloadPictures extends DefaultBoundedRangeModel implements Runnable {
|
||||
|
||||
// don't forget to remove new sets from ignore.urls to download (propeties file in resources)
|
||||
|
||||
private static DownloadPictures instance;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DownloadPictures.class);
|
||||
|
@ -300,7 +291,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
if (expansionSet != null) {
|
||||
setNames.add(expansionSet.getName());
|
||||
} else {
|
||||
logger.error(cardImageSource.getSourceName() + ": Expansion set for code " + setCode + " not found!");
|
||||
logger.warn("Source: " + cardImageSource.getSourceName() + ": Expansion set for code " + setCode + " not found in xmage sets!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,7 +343,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
if (cardImageSource.isTokenSource() && cardImageSource.isImageProvided(data.getSet(), data.getName())) {
|
||||
numberTokenImagesAvailable++;
|
||||
cardsToDownload.add(data);
|
||||
}else{
|
||||
} else {
|
||||
//logger.warn("Source do not support token (set " + data.getSet() + ", token " + data.getName() + ")");
|
||||
}
|
||||
} else {
|
||||
|
@ -433,7 +424,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
}
|
||||
|
||||
CardInfo secondSideCard = CardRepository.instance.findCard(card.getSecondSideName());
|
||||
if (secondSideCard == null){
|
||||
if (secondSideCard == null) {
|
||||
throw new IllegalStateException("Can''t find second side card in database: " + card.getSecondSideName());
|
||||
}
|
||||
|
||||
|
@ -473,6 +464,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
logger.debug(card.getName() + " (is_token=" + card.isToken() + "). Image is here:" + file.getAbsolutePath() + " (exists=" + file.exists() + ')');
|
||||
if (!file.exists()) {
|
||||
logger.debug("Missing: " + file.getAbsolutePath());
|
||||
// logger.info("Missing image: " + (card.isToken() ? "TOKEN " : "CARD ") + card.getSet() + "/" + card.getName() + " type: " + card.getType());
|
||||
cardsToDownload.add(card);
|
||||
}
|
||||
});
|
||||
|
@ -517,6 +509,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, "", "", true);
|
||||
card.setTokenClassName(tokenClassName);
|
||||
list.add(card);
|
||||
// logger.debug("Token: " + set + "/" + card.getName() + " type: " + type);
|
||||
} else if (params[1].toLowerCase().equals("generate") && params[2].startsWith("EMBLEM:")) {
|
||||
String set = params[2].substring(7);
|
||||
CardDownloadData card = new CardDownloadData("Emblem " + params[3], set, "0", false, type, "", "", true, fileName);
|
||||
|
@ -696,7 +689,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
TFile destFile;
|
||||
try {
|
||||
|
||||
if (card == null){
|
||||
if (card == null) {
|
||||
synchronized (sync) {
|
||||
update(cardIndex + 1, count);
|
||||
}
|
||||
|
@ -705,29 +698,31 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
|
||||
// gen temp file (download to images folder)
|
||||
String tempPath = getImagesDir() + File.separator + "downloading" + File.separator;
|
||||
if(useSpecifiedPaths){
|
||||
fileTempImage = new TFile(tempPath + actualFilename + "-" + card.hashCode() + ".jpg");
|
||||
}else{
|
||||
fileTempImage = new TFile(tempPath + CardImageUtils.prepareCardNameForFile(card.getName()) + "-" + card.hashCode() + ".jpg");
|
||||
if (useSpecifiedPaths) {
|
||||
fileTempImage = new TFile(tempPath + actualFilename + "-" + card.hashCode() + ".jpg");
|
||||
} else {
|
||||
fileTempImage = new TFile(tempPath + CardImageUtils.prepareCardNameForFile(card.getName()) + "-" + card.hashCode() + ".jpg");
|
||||
}
|
||||
if(!fileTempImage.getParentFile().exists()){
|
||||
fileTempImage.getParentFile().mkdirs();
|
||||
TFile parentFile = fileTempImage.getParentFile();
|
||||
if (parentFile != null) {
|
||||
if (!parentFile.exists()) {
|
||||
parentFile.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
// gen dest file name
|
||||
if(useSpecifiedPaths)
|
||||
{
|
||||
if(card.isToken()){
|
||||
if (useSpecifiedPaths) {
|
||||
if (card.isToken()) {
|
||||
destFile = new TFile(CardImageUtils.buildImagePathToSet(card) + actualFilename + ".jpg");
|
||||
}else{
|
||||
} else {
|
||||
destFile = new TFile(CardImageUtils.buildImagePathToTokens() + actualFilename + ".jpg");
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
destFile = new TFile(CardImageUtils.buildImagePathToCard(card));
|
||||
}
|
||||
|
||||
// FILE already exists (in zip or in dir)
|
||||
if (destFile.exists()){
|
||||
if (destFile.exists()) {
|
||||
synchronized (sync) {
|
||||
update(cardIndex + 1, count);
|
||||
}
|
||||
|
@ -753,9 +748,9 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
if(!destFile.getParentFile().exists()){
|
||||
destFile.getParentFile().mkdirs();
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
||||
/*
|
||||
/*
|
||||
// WTF start?! TODO: wtf
|
||||
File existingFile = new File(imagePath.replaceFirst("\\w{3}.zip", ""));
|
||||
if (existingFile.exists()) {
|
||||
|
@ -775,9 +770,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
return;
|
||||
}
|
||||
// WTF end?!
|
||||
*/
|
||||
|
||||
|
||||
*/
|
||||
// START to download
|
||||
cardImageSource.doPause(url.getPath());
|
||||
URLConnection httpConn = url.openConnection(p);
|
||||
|
@ -785,7 +778,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
httpConn.connect();
|
||||
int responseCode = ((HttpURLConnection) httpConn).getResponseCode();
|
||||
|
||||
if (responseCode == 200){
|
||||
if (responseCode == 200) {
|
||||
// download OK
|
||||
// save data to temp
|
||||
BufferedOutputStream out;
|
||||
|
@ -803,18 +796,18 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
// stop download, save current state and exit
|
||||
TFile archive = destFile.getTopLevelArchive();
|
||||
///* not need to unmout/close - it's auto action
|
||||
if (archive != null && archive.exists()){
|
||||
if (archive != null && archive.exists()) {
|
||||
logger.info("User canceled download. Closing archive file: " + destFile.toString());
|
||||
try {
|
||||
TVFS.umount(archive);
|
||||
}catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
logger.error("Can't close archive file: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
}//*/
|
||||
try {
|
||||
TFile.rm(fileTempImage);
|
||||
}catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
logger.error("Can't delete temp file: " + e.getMessage(), e);
|
||||
}
|
||||
return;
|
||||
|
@ -827,25 +820,24 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
out.close();
|
||||
|
||||
// TODO: add two faces card correction? (WTF)
|
||||
|
||||
// SAVE final data
|
||||
if (fileTempImage.exists()) {
|
||||
if (!destFile.getParentFile().exists()){
|
||||
if (!destFile.getParentFile().exists()) {
|
||||
destFile.getParentFile().mkdirs();
|
||||
}
|
||||
new TFile(fileTempImage).cp_rp(destFile);
|
||||
try {
|
||||
TFile.rm(fileTempImage);
|
||||
}catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
logger.error("Can't delete temp file: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
// download ERROR
|
||||
logger.warn("Image download for " + card.getName()
|
||||
+ (!card.getDownloadName().equals(card.getName()) ? " downloadname: " + card.getDownloadName() : "")
|
||||
+ " (" + card.getSet() + ") failed - responseCode: " + responseCode + " url: " + url.toString()
|
||||
+ (!card.getDownloadName().equals(card.getName()) ? " downloadname: " + card.getDownloadName() : "")
|
||||
+ " (" + card.getSet() + ") failed - responseCode: " + responseCode + " url: " + url.toString()
|
||||
);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
@ -928,8 +920,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
logger.debug("Returned HTML ERROR:\n" + convertStreamToString(((HttpURLConnection) httpConn).getErrorStream()));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
*/
|
||||
} catch (AccessDeniedException e) {
|
||||
logger.error("Can't access to files: " + card.getName() + "(" + card.getSet() + "). Try rebooting your system to remove the file lock.");
|
||||
} catch (Exception e) {
|
||||
|
@ -942,25 +933,25 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
|
|||
}
|
||||
}
|
||||
|
||||
private void writeImageToFile(BufferedImage image, TFile file) throws IOException {
|
||||
Iterator iter = ImageIO.getImageWritersByFormatName("jpg");
|
||||
|
||||
ImageWriter writer = (ImageWriter) iter.next();
|
||||
ImageWriteParam iwp = writer.getDefaultWriteParam();
|
||||
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
iwp.setCompressionQuality(0.96f);
|
||||
|
||||
File tempFile = new File(getImagesDir() + File.separator + image.hashCode() + file.getName());
|
||||
FileImageOutputStream output = new FileImageOutputStream(tempFile);
|
||||
writer.setOutput(output);
|
||||
IIOImage image2 = new IIOImage(image, null, null);
|
||||
writer.write(null, image2, iwp);
|
||||
writer.dispose();
|
||||
output.close();
|
||||
|
||||
new TFile(tempFile).cp_rp(file);
|
||||
tempFile.delete();
|
||||
}
|
||||
// private void writeImageToFile(BufferedImage image, TFile file) throws IOException {
|
||||
// Iterator iter = ImageIO.getImageWritersByFormatName("jpg");
|
||||
//
|
||||
// ImageWriter writer = (ImageWriter) iter.next();
|
||||
// ImageWriteParam iwp = writer.getDefaultWriteParam();
|
||||
// iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
// iwp.setCompressionQuality(0.96f);
|
||||
//
|
||||
// File tempFile = new File(getImagesDir() + File.separator + image.hashCode() + file.getName());
|
||||
// FileImageOutputStream output = new FileImageOutputStream(tempFile);
|
||||
// writer.setOutput(output);
|
||||
// IIOImage image2 = new IIOImage(image, null, null);
|
||||
// writer.write(null, image2, iwp);
|
||||
// writer.dispose();
|
||||
// output.close();
|
||||
//
|
||||
// new TFile(tempFile).cp_rp(file);
|
||||
// tempFile.delete();
|
||||
// }
|
||||
}
|
||||
|
||||
private void update(int card, int count) {
|
||||
|
|
|
@ -56,8 +56,7 @@ public final class CardImageUtils {
|
|||
*/
|
||||
public static String generateFullTokenImagePath(CardDownloadData card) {
|
||||
if (card.isToken()) {
|
||||
String filePath = getTokenImagePath(card);
|
||||
return filePath;
|
||||
return getTokenImagePath(card);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
@ -67,23 +66,29 @@ public final class CardImageUtils {
|
|||
|
||||
TFile file = new TFile(filename);
|
||||
if (!file.exists()) {
|
||||
filename = generateTokenDescriptorImagePath(card);
|
||||
}
|
||||
|
||||
file = new TFile(filename);
|
||||
if (!file.exists()) {
|
||||
CardDownloadData updated = new CardDownloadData(card);
|
||||
updated.setName(card.getName() + " 1");
|
||||
filename = buildImagePathToCard(updated);
|
||||
file = new TFile(filename);
|
||||
if (!file.exists()) {
|
||||
updated = new CardDownloadData(card);
|
||||
updated.setName(card.getName() + " 2");
|
||||
filename = buildImagePathToCard(updated);
|
||||
String tokenDescriptorfilename = generateTokenDescriptorImagePath(card);
|
||||
if (!tokenDescriptorfilename.isEmpty()) {
|
||||
file = new TFile(filename);
|
||||
if (file.exists()) {
|
||||
return tokenDescriptorfilename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filename;
|
||||
|
||||
// makes no longer sense
|
||||
// file = new TFile(filename);
|
||||
// if (!file.exists()) {
|
||||
// CardDownloadData updated = new CardDownloadData(card);
|
||||
// updated.setName(card.getName() + " 1");
|
||||
// filename = buildImagePathToCard(updated);
|
||||
// file = new TFile(filename);
|
||||
// if (!file.exists()) {
|
||||
// updated = new CardDownloadData(card);
|
||||
// updated.setName(card.getName() + " 2");
|
||||
// filename = buildImagePathToCard(updated);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private static String searchForCardImage(CardDownloadData card) {
|
||||
|
@ -208,7 +213,7 @@ public final class CardImageUtils {
|
|||
if (card.getUsesVariousArt()) {
|
||||
finalFileName = cardName + '.' + card.getCollectorId() + ".full.jpg";
|
||||
} else {
|
||||
if (card.getUsesVariousArt()){
|
||||
if (card.getUsesVariousArt()) {
|
||||
// only various arts can be same name, but different postfixes (a,b,c,d,e)
|
||||
int len = card.getCollectorId().length();
|
||||
if (Character.isLetter(card.getCollectorId().charAt(len - 1))) {
|
||||
|
|
|
@ -45,10 +45,10 @@
|
|||
#|Generate|TOK:PTC|Wolf||
|
||||
#|Generate|TOK:PTC|Wurm||
|
||||
#|Generate|TOK:WMCQ|Angel||
|
||||
|Generate|EMBLEM!:AKH|Emblem Gideon|||GideonOfTheTrialsEmblem|
|
||||
|Generate|EMBLEM!:BFZ|Emblem Gideon|||GideonAllyOfZendikarEmblem|
|
||||
|Generate|EMBLEM!:BFZ|Emblem Kiora|||KioraMasterOfTheDepthsEmblem|
|
||||
|Generate|EMBLEM!:BFZ|Emblem Nixilis|||ObNixilisReignitedEmblem|
|
||||
|Generate|EMBLEM!:AKH|Emblem Gideon|||GideonOfTheTrialsEmblem|
|
||||
|Generate|EMBLEM!:C14|Emblem Daretti|||DarettiScrapSavantEmblem|
|
||||
|Generate|EMBLEM!:C14|Emblem Daretti||Emblem Daretti|DarettiScrapSavantEmblem|
|
||||
|Generate|EMBLEM!:C14|Emblem Nixilis|||ObNixilisOfTheBlackOathEmblem|
|
||||
|
@ -73,7 +73,7 @@
|
|||
|Generate|EMBLEM!:ORI|Emblem Liliana|||LilianaDefiantNecromancerEmblem|
|
||||
|Generate|EMBLEM!:SOI|Emblem Arlinn|||ArlinnEmbracedByTheMoonEmblem|
|
||||
|Generate|EMBLEM!:SOI|Emblem Jace|||JaceUnravelerOfSecretsEmblem|
|
||||
|Generate|EMBLEM-:THS|Elspeth, Suns Champion||Emblem Elspeth|ElspethSunsChampionEmblem|
|
||||
|Generate|EMBLEM:THS|Elspeth, Suns Champion||Emblem Elspeth|ElspethSunsChampionEmblem|
|
||||
|Generate|EMBLEM:AVR|Tamiyo, the Moon Sage||Emblem Tamiyo|TamiyoTheMoonSageEmblem|
|
||||
|Generate|EMBLEM:BNG|Kiora, the Crashing Wave||Emblem Kiora|KioraEmblem|
|
||||
|Generate|EMBLEM:DDI|Koth of the Hammer||Emblem Koth|KothOfTheHammerEmblem|
|
||||
|
@ -484,8 +484,8 @@
|
|||
|Generate|TOK:DKA|Zombie|||ZombieToken|
|
||||
|Generate|TOK:DRB|Saproling|||SaprolingToken|
|
||||
|Generate|TOK:DST|Beast|||BeastToken|
|
||||
|Generate|TOK:DST|Elemental|||WandOfTheElementsFirstToken|
|
||||
|Generate|TOK:DST|Elemental|||WandOfTheElementsSecondToken|
|
||||
|Generate|TOK:DST|Elemental|1||WandOfTheElementsFirstToken|
|
||||
|Generate|TOK:DST|Elemental|2||WandOfTheElementsSecondToken|
|
||||
|Generate|TOK:DST|InsectWirefly|||WireflyToken|
|
||||
|Generate|TOK:DST|Insect|||InsectToken|
|
||||
|Generate|TOK:DST|Myr|||MyrToken|
|
||||
|
@ -731,7 +731,7 @@
|
|||
|Generate|TOK:M15|Beast|2||GarrukApexPredatorBeastToken|
|
||||
|Generate|TOK:M15|Dragon|||BroodKeeperDragonToken|
|
||||
|Generate|TOK:M15|Goblin|1||GoblinTokenWithHaste|
|
||||
|Generate|TOK:M15|Goblin|2||GoblinToken|
|
||||
# |Generate|TOK:M15|Goblin|2||GoblinToken| #does not exist
|
||||
|Generate|TOK:M15|Insect|||InsectToken|
|
||||
|Generate|TOK:M15|Land Mine|||LandMineToken|
|
||||
|Generate|TOK:M15|Sliver|||SliverToken|
|
||||
|
@ -1000,8 +1000,7 @@
|
|||
|Generate|TOK:SOM|Cat|||CatToken|
|
||||
|Generate|TOK:SOM|Goblin|||GoblinToken|
|
||||
|Generate|TOK:SOM|Golem|||GolemToken|
|
||||
|Generate|TOK:SOM|Insect|1||InsectToken|
|
||||
|Generate|TOK:SOM|Insect|2||InsectInfectToken|
|
||||
|Generate|TOK:SOM|Insect|||InsectInfectToken|
|
||||
|Generate|TOK:SOM|Myr|||MyrToken|
|
||||
|Generate|TOK:SOM|Soldier|||SoldierToken|
|
||||
|Generate|TOK:SOM|Wolf|||WolfToken|
|
||||
|
@ -1068,7 +1067,7 @@
|
|||
|Generate|TOK:USG|Minion|||MinionToken|
|
||||
|Generate|TOK:USG|Saproling|||SaprolingToken|
|
||||
|Generate|TOK:UST|Dragon|||DragonTokenGold|
|
||||
|Generate|TOK:UST|StormCrow|||StormCrowToken|
|
||||
|Generate|TOK:UST|Storm Crow|||StormCrowToken|
|
||||
|Generate|TOK:V10|Wolf|||WolfToken|
|
||||
|Generate|TOK:V11|Faerie Rogue|||OonaQueenFaerieToken|
|
||||
|Generate|TOK:V12|Spirit|||SpiritToken|
|
||||
|
|
|
@ -29,6 +29,7 @@ package mage.deck;
|
|||
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import mage.cards.ExpansionSet;
|
||||
import mage.cards.Sets;
|
||||
import mage.cards.decks.Constructed;
|
||||
|
@ -54,7 +55,6 @@ public class Modern extends Constructed {
|
|||
banned.add("Ancient Den");
|
||||
banned.add("Birthing Pod");
|
||||
banned.add("Blazing Shoal");
|
||||
banned.add("Bloodbraid Elf");
|
||||
banned.add("Chrome Mox");
|
||||
banned.add("Cloudpost");
|
||||
banned.add("Dark Depths");
|
||||
|
@ -68,7 +68,6 @@ public class Modern extends Constructed {
|
|||
banned.add("Great Furnace");
|
||||
banned.add("Green Sun's Zenith");
|
||||
banned.add("Hypergenesis");
|
||||
banned.add("Jace, the Mind Sculptor");
|
||||
banned.add("Mental Misstep");
|
||||
banned.add("Ponder");
|
||||
banned.add("Preordain");
|
||||
|
|
|
@ -52,6 +52,7 @@ import mage.cards.repository.CardRepository;
|
|||
import mage.choices.Choice;
|
||||
import mage.choices.ChoiceColor;
|
||||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.*;
|
||||
|
@ -143,7 +144,6 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
|
||||
@Override
|
||||
public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map<String, Serializable> options) {
|
||||
System.out.println("choose in ComputerPlayer");
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("chooseTarget: " + outcome.toString() + ':' + target.toString());
|
||||
}
|
||||
|
@ -397,7 +397,6 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
|
||||
@Override
|
||||
public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) {
|
||||
System.out.println("chooseTarget in ComputerPlayer");
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("chooseTarget: " + outcome.toString() + ':' + target.toString());
|
||||
}
|
||||
|
@ -756,7 +755,6 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
} //end of chooseTarget method
|
||||
|
||||
protected Card pickTarget(List<Card> cards, Outcome outcome, Target target, Ability source, Game game) {
|
||||
System.out.println("pickTarget in ComputerPlayer");
|
||||
Card card;
|
||||
while (!cards.isEmpty()) {
|
||||
if (outcome.isGood()) {
|
||||
|
@ -778,7 +776,6 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
|
||||
@Override
|
||||
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) {
|
||||
System.out.println("chooseTargetAmount in ComputerPlayer");
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("chooseTarget: " + outcome.toString() + ':' + target.toString());
|
||||
}
|
||||
|
@ -813,13 +810,59 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
target.addTarget(opponentId, target.getAmountRemaining(), source, game);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (target.getOriginalTarget() instanceof TargetCreatureOrPlaneswalkerAmount) {
|
||||
List<Permanent> targets;
|
||||
if (outcome.isGood()) {
|
||||
targets = threats(playerId, source.getSourceId(), StaticFilters.FILTER_PERMANENT_CREATURE, game, target.getTargets());
|
||||
} else {
|
||||
targets = threats(opponentId, source.getSourceId(), StaticFilters.FILTER_PERMANENT_CREATURE, game, target.getTargets());
|
||||
}
|
||||
for (Permanent permanent : targets) {
|
||||
if (target.canTarget(getId(), permanent.getId(), source, game)) {
|
||||
if (permanent.getToughness().getValue() <= target.getAmountRemaining()) {
|
||||
target.addTarget(permanent.getId(), permanent.getToughness().getValue(), source, game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target.getFilter() instanceof FilterPermanent) {
|
||||
targets = threats(null, source.getSourceId(), (FilterPermanent) target.getFilter(), game, target.getTargets());
|
||||
Permanent possibleTarget = null;
|
||||
for (Permanent permanent : targets) {
|
||||
if (target.canTarget(getId(), permanent.getId(), source, game)) {
|
||||
if (permanent.isCreature()) {
|
||||
if (permanent.getToughness().getValue() <= target.getAmountRemaining()) {
|
||||
target.addTarget(permanent.getId(), permanent.getToughness().getValue(), source, game);
|
||||
return true;
|
||||
} else {
|
||||
possibleTarget = permanent;
|
||||
}
|
||||
} else if (permanent.isPlaneswalker()) {
|
||||
int loy = permanent.getCounters(game).getCount(CounterType.LOYALTY);
|
||||
if (loy <= target.getAmountRemaining()) {
|
||||
target.addTarget(permanent.getId(), loy, source, game);
|
||||
return true;
|
||||
} else {
|
||||
possibleTarget = permanent;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (possibleTarget != null) {
|
||||
target.addTarget(possibleTarget.getId(), target.getAmountRemaining(), source, game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.warn("No proper AI target handling: " + target.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean priority(Game game) {
|
||||
System.out.println("priority in ComputerPlayer");
|
||||
game.resumeTimer(getTurnControlledBy());
|
||||
log.debug("priority");
|
||||
boolean result = priorityPlay(game);
|
||||
|
@ -828,7 +871,6 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
}
|
||||
|
||||
private boolean priorityPlay(Game game) {
|
||||
System.out.println("priorityPlay in ComputerPlayer");
|
||||
UUID opponentId = game.getOpponents(playerId).iterator().next();
|
||||
if (game.getActivePlayerId().equals(playerId)) {
|
||||
if (game.isMainPhase() && game.getStack().isEmpty()) {
|
||||
|
@ -985,7 +1027,6 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
}
|
||||
|
||||
protected void findPlayables(Game game) {
|
||||
System.out.println("Here in findPlayables");
|
||||
playableInstant.clear();
|
||||
playableNonInstant.clear();
|
||||
unplayable.clear();
|
||||
|
|
|
@ -48,7 +48,7 @@ import mage.game.events.GameEvent.EventType;
|
|||
public class ConsecratedSphinx extends CardImpl {
|
||||
|
||||
public ConsecratedSphinx(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}{U}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}");
|
||||
this.subtype.add(SubType.SPHINX);
|
||||
|
||||
this.power = new MageInt(4);
|
||||
|
@ -94,7 +94,8 @@ class ConsecratedSphinxTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return game.getOpponents(this.getControllerId()).contains(event.getPlayerId());
|
||||
return game.getOpponents(getControllerId()).contains(event.getPlayerId())
|
||||
&& game.getPermanent(sourceId) != null; // the Sphinx must be on the battlefield
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -151,10 +151,7 @@ class GoldenGuardianDelayedTriggeredAbility extends DelayedTriggeredAbility {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (((ZoneChangeEvent) event).isDiesEvent()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return ((ZoneChangeEvent) event).isDiesEvent() && event.getTargetId().equals(getSourceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,6 +27,12 @@
|
|||
*/
|
||||
package mage.cards.m;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.*;
|
||||
|
@ -34,17 +40,13 @@ import mage.constants.CardType;
|
|||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
|
@ -52,8 +54,7 @@ import java.util.UUID;
|
|||
public class MitoticManipulation extends CardImpl {
|
||||
|
||||
public MitoticManipulation(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{U}{U}");
|
||||
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}{U}");
|
||||
|
||||
this.getSpellAbility().addEffect(new MitoticManipulationEffect());
|
||||
}
|
||||
|
@ -86,45 +87,39 @@ class MitoticManipulationEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
List<Permanent> permanents = game.getBattlefield().getActivePermanents(source.getControllerId(), game);
|
||||
Set<String> permanentNames = new HashSet<>();
|
||||
FilterCard filter = new FilterCard("card to put onto the battlefield");
|
||||
for (Permanent permanent : permanents) {
|
||||
permanentNames.add(permanent.getName());
|
||||
filter.add(new NamePredicate(permanent.getName()));
|
||||
}
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
if (controller != null && sourceObject != null) {
|
||||
Set<String> permanentNames = new HashSet<>();
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) {
|
||||
permanentNames.add(permanent.getName());
|
||||
}
|
||||
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Cards cards = new CardsImpl();
|
||||
Cards cardsFound = new CardsImpl();
|
||||
int count = Math.min(player.getLibrary().size(), 7);
|
||||
for (int i = 0; i < count; i++) {
|
||||
Card card = player.getLibrary().removeFromTop(game);
|
||||
if (card != null) {
|
||||
cards.add(card);
|
||||
if (permanentNames.contains(card.getName())) {
|
||||
cardsFound.add(card);
|
||||
Cards cardsFromTop = new CardsImpl();
|
||||
cardsFromTop.addAll(controller.getLibrary().getTopCards(game, 7));
|
||||
controller.lookAtCards(sourceObject.getIdName(), cardsFromTop, game);
|
||||
FilterCard filter = new FilterCard("card to put onto the battlefield");
|
||||
List<NamePredicate> namePredicates = new ArrayList<>();
|
||||
for (String name : permanentNames) {
|
||||
namePredicates.add(new NamePredicate(name));
|
||||
}
|
||||
if (!namePredicates.isEmpty() && !cardsFromTop.isEmpty()) {
|
||||
filter.add(Predicates.or(namePredicates));
|
||||
TargetCard target = new TargetCard(Zone.LIBRARY, filter);
|
||||
if (cardsFromTop.count(filter, source.getSourceId(), source.getControllerId(), game) > 0
|
||||
&& controller.chooseUse(Outcome.PutCardInPlay, "Do you wish to put a card on the battlefield?", source, game)) {
|
||||
if (controller.choose(Outcome.PutCardInPlay, cardsFromTop, target, game)) {
|
||||
Card card = cardsFromTop.get(target.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
controller.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
cardsFromTop.remove(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
controller.putCardsOnBottomOfLibrary(cardsFromTop, game, source, true);
|
||||
return true;
|
||||
}
|
||||
player.lookAtCards("Mitotic Manipulation", cards, game);
|
||||
|
||||
if (!cardsFound.isEmpty() && player.chooseUse(Outcome.PutCardInPlay, "Do you wish to put a card on the battlefield?", source, game)) {
|
||||
TargetCard target = new TargetCard(Zone.LIBRARY, filter);
|
||||
|
||||
if (player.choose(Outcome.PutCardInPlay, cardsFound, target, game)) {
|
||||
Card card = cards.get(target.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
cards.remove(card);
|
||||
card.putOntoBattlefield(game, Zone.LIBRARY, source.getSourceId(), source.getControllerId());
|
||||
}
|
||||
}
|
||||
}
|
||||
player.putCardsOnBottomOfLibrary(cards, game, source, true);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,9 +59,13 @@ public class WandOfTheElements extends CardImpl {
|
|||
|
||||
public WandOfTheElements(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
|
||||
|
||||
// {T}, Sacrifice an Island: Create a 2/2 blue Elemental creature token with flying.
|
||||
Ability firstAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new WandOfTheElementsFirstToken()), new TapSourceCost());
|
||||
firstAbility.addCost(new SacrificeTargetCost(new TargetControlledPermanent(islandFilter)));
|
||||
this.addAbility(firstAbility);
|
||||
|
||||
// {T}, Sacrifice a Mountain: Create a 3/3 red Elemental creature token.
|
||||
Ability secondAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new WandOfTheElementsSecondToken()), new TapSourceCost());
|
||||
secondAbility.addCost(new SacrificeTargetCost(new TargetControlledPermanent(mountainFilter)));
|
||||
this.addAbility(secondAbility);
|
||||
|
|
|
@ -27,7 +27,10 @@
|
|||
*/
|
||||
package org.mage.test.cards.abilities.keywords;
|
||||
|
||||
import mage.abilities.mana.ManaOptions;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.permanent.Permanent;
|
||||
import org.junit.Assert;
|
||||
|
@ -445,4 +448,48 @@ public class BestowTest extends CardTestPlayerBase {
|
|||
assertTapped("Elite Vanguard", true);
|
||||
assertPowerToughness(playerA, "Elite Vanguard", 5, 3); // 2/1 + 3/2 = 5/3
|
||||
}
|
||||
|
||||
/**
|
||||
* When a creature with Nighthowler attatched gets enchanted with Song of
|
||||
* the Dryads, Nightholwer doesn't become a creature and gets turned into a
|
||||
* card without stats.
|
||||
*/
|
||||
@Test
|
||||
public void testEnchantedChangedWithSongOfTheDryads() {
|
||||
// Enchantment Creature — Horror
|
||||
// 0/0
|
||||
// Bestow {2}{B}{B}
|
||||
// Nighthowler and enchanted creature each get +X/+X, where X is the number of creature cards in all graveyards.
|
||||
addCard(Zone.HAND, playerA, "Nighthowler");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // {1}{W} 2/2 creature
|
||||
|
||||
addCard(Zone.GRAVEYARD, playerA, "Pillarfield Ox");
|
||||
addCard(Zone.GRAVEYARD, playerB, "Pillarfield Ox");
|
||||
|
||||
// Enchant permanent
|
||||
// Enchanted permanent is a colorless Forest land.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Forest", 3);
|
||||
addCard(Zone.HAND, playerB, "Song of the Dryads"); // Enchantment Aura {2}{G}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nighthowler using bestow", "Silvercoat Lion");
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Song of the Dryads", "Silvercoat Lion");
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerB, "Song of the Dryads", 1);
|
||||
|
||||
ManaOptions options = playerA.getAvailableManaTest(currentGame);
|
||||
Assert.assertEquals("Player should be able to create 1 green mana", "{G}", options.get(0).toString());
|
||||
|
||||
assertPermanentCount(playerA, "Nighthowler", 1);
|
||||
assertPowerToughness(playerA, "Nighthowler", 2, 2);
|
||||
assertType("Nighthowler", CardType.CREATURE, true);
|
||||
assertType("Nighthowler", CardType.ENCHANTMENT, true);
|
||||
|
||||
Permanent nighthowler = getPermanent("Nighthowler");
|
||||
Assert.assertFalse("The unattached Nighthowler may not have the aura subtype.", nighthowler.getSubtype(currentGame).contains(SubType.AURA));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -254,6 +254,14 @@ public class SubTypeChangingEffectsTest extends CardTestPlayerBase {
|
|||
// Creatures you control are the chosen type in addition to their other types. The same is true for creature spells you control and creature cards you own that aren't on the battlefield.
|
||||
addCard(Zone.HAND, playerA, "Arcane Adaptation", 1); // Enchantment {2}{U}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 8);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
|
||||
|
||||
// Create a 5/5 green Wurm creature token with trample.
|
||||
addCard(Zone.HAND, playerA, "Advent of the Wurm", 1); // Instant {1}{G}{G}{W}
|
||||
// Create a 4/4 green Beast creature token.
|
||||
// Flashback {2}{G}{G}{G} (You may cast this card from your graveyard for its flashback cost. Then exile it.)
|
||||
addCard(Zone.HAND, playerA, "Beast Attack", 1); // Instant {2}{G}{G}{G}
|
||||
|
||||
addCard(Zone.HAND, playerA, "Silvercoat Lion");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
|
||||
|
@ -266,21 +274,35 @@ public class SubTypeChangingEffectsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
|
||||
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");
|
||||
|
||||
castSpell(1, PhaseStep.UPKEEP, playerA, "Advent of the Wurm");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Arcane Adaptation");
|
||||
setChoice(playerA, "Orc");
|
||||
|
||||
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Beast Attack");
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Disenchant", "Arcane Adaptation");
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Arcane Adaptation", 1);
|
||||
assertGraveyardCount(playerA, "Advent of the Wurm", 1);
|
||||
assertGraveyardCount(playerA, "Beast Attack", 1);
|
||||
assertGraveyardCount(playerB, "Disenchant", 1);
|
||||
|
||||
Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA);
|
||||
Assert.assertEquals(true, silvercoatLion.getSubtype(currentGame).contains(SubType.CAT));
|
||||
Assert.assertEquals(false, silvercoatLion.getSubtype(currentGame).contains(SubType.ORC));
|
||||
|
||||
Permanent beast = getPermanent("Beast", playerA);
|
||||
Assert.assertEquals(true, beast.getSubtype(currentGame).contains(SubType.BEAST));
|
||||
Assert.assertEquals(false, beast.getSubtype(currentGame).contains(SubType.ORC));
|
||||
|
||||
Permanent wurm = getPermanent("Wurm", playerA);
|
||||
Assert.assertEquals(true, wurm.getSubtype(currentGame).contains(SubType.WURM));
|
||||
Assert.assertEquals(false, wurm.getSubtype(currentGame).contains(SubType.ORC));
|
||||
|
||||
for (Card card : playerA.getLibrary().getCards(currentGame)) {
|
||||
if (card.isCreature()) {
|
||||
Assert.assertEquals(card.getName() + " should not have ORC type", false, card.getSubtype(currentGame).contains(SubType.ORC));
|
||||
|
|
|
@ -154,4 +154,67 @@ public class AnimateDeadTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerA, "Animate Dead", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate Dead enchanting a creature with a "enters the battlefield"
|
||||
* triggered ability with targets available.
|
||||
*/
|
||||
@Test
|
||||
public void testAnimateAndDragonlordAtarkaWithTargets() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||
|
||||
// Enchant creature card in a graveyard
|
||||
// When Animate Dead enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard"
|
||||
// and gains "enchant creature put onto the battlefield with Animate Dead." Return enchanted creature card to the battlefield
|
||||
// under your control and attach Animate Dead to it. When Animate Dead leaves the battlefield, that creature's controller sacrifices it.
|
||||
// Enchanted creature gets -1/-0.
|
||||
addCard(Zone.HAND, playerA, "Animate Dead"); // {1}{B}
|
||||
|
||||
// Flying
|
||||
// Trample
|
||||
// When Dragonlord Atarka enters the battlefield, it deals 5 damage divided as you choose among any number of target
|
||||
// creatures and/or planeswalkers your opponents control.
|
||||
addCard(Zone.GRAVEYARD, playerB, "Dragonlord Atarka", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 3);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Dead", "Dragonlord Atarka");
|
||||
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Animate Dead", 1);
|
||||
assertPermanentCount(playerA, "Dragonlord Atarka", 1);
|
||||
|
||||
assertPermanentCount(playerB, "Silvercoat Lion", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate Dead enchanting a creature with a "enters the battlefield"
|
||||
* triggered ability with no available targets.
|
||||
* https://github.com/magefree/mage/issues/4428
|
||||
*/
|
||||
@Test
|
||||
public void testAnimateAndDragonlordAtarkaWithNoTargets() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||
|
||||
// Enchant creature card in a graveyard
|
||||
// When Animate Dead enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard"
|
||||
// and gains "enchant creature put onto the battlefield with Animate Dead." Return enchanted creature card to the battlefield
|
||||
// under your control and attach Animate Dead to it. When Animate Dead leaves the battlefield, that creature's controller sacrifices it.
|
||||
// Enchanted creature gets -1/-0.
|
||||
addCard(Zone.HAND, playerA, "Animate Dead"); // {1}{B}
|
||||
|
||||
// Flying
|
||||
// Trample
|
||||
// When Dragonlord Atarka enters the battlefield, it deals 5 damage divided as you choose among any number of target
|
||||
// creatures and/or planeswalkers your opponents control.
|
||||
addCard(Zone.GRAVEYARD, playerB, "Dragonlord Atarka", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Dead", "Dragonlord Atarka");
|
||||
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Animate Dead", 1);
|
||||
assertPermanentCount(playerA, "Dragonlord Atarka", 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.test.cards.enchantments;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class AuraTargetRemovedTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* Spreading Seas is bugged, opp casted it on my Field of Ruin, I sacced
|
||||
* with the spell on stack but it resolved anyway and let him draw.
|
||||
*
|
||||
* 303.4. Some enchantments have the subtype “Aura.” An Aura enters the
|
||||
* battlefield attached to an object or player. What an Aura can be attached
|
||||
* to is restricted by its enchant keyword ability (see rule 702.5,
|
||||
* “Enchant”). Other effects can limit what a permanent can be enchanted by.
|
||||
* 303.4a An Aura spell requires a target, which is restricted by its
|
||||
* enchant ability.
|
||||
*/
|
||||
@Test
|
||||
public void testOneAttackerDamage() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Academy Ruins", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
// Enchant land
|
||||
// When Spreading Seas enters the battlefield, draw a card.
|
||||
// Enchanted land is an Island.
|
||||
addCard(Zone.HAND, playerA, "Spreading Seas", 1); //Enchantment {1}{U}
|
||||
|
||||
// {T}: Add {C} to your mana pool.
|
||||
// {2}, {T}, Sacrifice Field of Ruin: Destroy target nonbasic land an opponent controls. Each player searches his or her library for a basic land card, puts it onto the battlefield, then shuffles his or her library.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Field of Ruin", 1); //
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spreading Seas", "Field of Ruin");
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{2}, {T}", "Academy Ruins", "Spreading Seas");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerB, 3);
|
||||
assertGraveyardCount(playerB, "Field of Ruin", 1);
|
||||
|
||||
assertPermanentCount(playerA, 3);
|
||||
assertGraveyardCount(playerA, "Spreading Seas", 1);
|
||||
assertGraveyardCount(playerA, "Academy Ruins", 1);
|
||||
|
||||
assertHandCount(playerA, 0); // Because Spreading Seas is counterd (no valid target), no card is drawn from it
|
||||
}
|
||||
|
||||
}
|
|
@ -198,7 +198,7 @@ public enum SubType {
|
|||
LHURGOYF("Lhurgoyf", SubTypeSet.CreatureType),
|
||||
LICID("Licid", SubTypeSet.CreatureType),
|
||||
LIZARD("Lizard", SubTypeSet.CreatureType),
|
||||
LOBSTER("Lobster", SubTypeSet.CreatureType, true), // Unglued
|
||||
LOBSTER("Lobster", SubTypeSet.CreatureType, true), // Unglued
|
||||
// M
|
||||
MANTELLIAN("Mantellian", SubTypeSet.CreatureType, true), // Star Wars
|
||||
MANTICORE("Manticore", SubTypeSet.CreatureType),
|
||||
|
@ -442,6 +442,16 @@ public enum SubType {
|
|||
return subTypeSet;
|
||||
}
|
||||
|
||||
public static Set<SubType> getPlaneswalkerTypes(boolean withCustomSets) {
|
||||
Set<SubType> subTypes = EnumSet.noneOf(SubType.class);
|
||||
for (SubType subType : values()) {
|
||||
if (subType.getSubTypeSet() == SubTypeSet.PlaneswalkerType && (withCustomSets || !subType.customSet)) {
|
||||
subTypes.add(subType);
|
||||
}
|
||||
}
|
||||
return subTypes;
|
||||
}
|
||||
|
||||
public static Set<SubType> getCreatureTypes(boolean customSet) {
|
||||
Set<SubType> subTypes = EnumSet.noneOf(SubType.class);
|
||||
for (SubType s : values()) {
|
||||
|
|
|
@ -1884,6 +1884,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
if (card != null && card.isCreature()) {
|
||||
UUID wasAttachedTo = perm.getAttachedTo();
|
||||
perm.attachTo(null, this);
|
||||
BestowAbility.becomeCreature(perm, this);
|
||||
fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, wasAttachedTo, perm.getId(), perm.getControllerId()));
|
||||
} else if (movePermanentToGraveyardWithInfo(perm)) {
|
||||
somethingHappened = true;
|
||||
|
@ -1909,7 +1910,9 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
if (attachedTo == null
|
||||
|| !((TargetCard) spellAbility.getTargets().get(0)).canTarget(perm.getControllerId(), perm.getAttachedTo(), spellAbility, this)) {
|
||||
if (movePermanentToGraveyardWithInfo(perm)) {
|
||||
attachedTo.removeAttachment(perm.getId(), this);
|
||||
if (attachedTo != null) {
|
||||
attachedTo.removeAttachment(perm.getId(), this);
|
||||
}
|
||||
somethingHappened = true;
|
||||
}
|
||||
}
|
||||
|
@ -2953,6 +2956,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
executingRollback = true;
|
||||
for (Player playerObject : getPlayers().values()) {
|
||||
if (playerObject.isHuman() && playerObject.isInGame()) {
|
||||
playerObject.resetStoredBookmark(this);
|
||||
playerObject.abort();
|
||||
playerObject.resetPlayerPassedActions();
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ import mage.game.events.GameEvent;
|
|||
public class GideonOfTheTrialsEmblem extends Emblem {
|
||||
|
||||
public GideonOfTheTrialsEmblem() {
|
||||
this.setName("Emblem - Gideon of the Trials");
|
||||
this.setName("Emblem - Gideon");
|
||||
Ability ability = new SimpleStaticAbility(Zone.COMMAND, new GideonOfTheTrialsCantLoseEffect());
|
||||
this.getAbilities().add(ability);
|
||||
}
|
||||
|
|
|
@ -88,8 +88,10 @@ public class PermanentToken extends PermanentImpl {
|
|||
this.color = token.getColor(game).copy();
|
||||
this.frameColor = token.getFrameColor(game);
|
||||
this.frameStyle = token.getFrameStyle();
|
||||
this.supertype = token.getSuperType();
|
||||
this.subtype = token.getSubtype(game);
|
||||
this.supertype.clear();
|
||||
this.supertype.addAll(token.getSuperType());
|
||||
this.subtype.clear();
|
||||
this.subtype.addAll(token.getSubtype(game));
|
||||
this.tokenDescriptor = token.getTokenDescriptor();
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ public class RekindlingPhoenixToken extends Token {
|
|||
|
||||
public RekindlingPhoenixToken() {
|
||||
super("Elemental", "0/1 red Elemental creature token with \"At the beginning of your upkeep, sacrifice this creature and return target card named Rekindling Phoenix from your graveyard to the battlefield. It gains haste until end of turn.\"");
|
||||
setTokenType(1);
|
||||
cardType.add(CardType.CREATURE);
|
||||
subtype.add(SubType.THRULL);
|
||||
color.setRed(true);
|
||||
|
|
|
@ -39,6 +39,7 @@ public class TilonallisSummonerElementalToken extends Token {
|
|||
|
||||
public TilonallisSummonerElementalToken() {
|
||||
super("Elemental", "1/1 red Elemental creature tokens");
|
||||
setTokenType(2);
|
||||
cardType.add(CardType.CREATURE);
|
||||
subtype.add(SubType.ELEMENTAL);
|
||||
|
||||
|
|
|
@ -25,12 +25,12 @@
|
|||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
package mage.game.permanent.token;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -40,6 +40,7 @@ public class WandOfTheElementsFirstToken extends Token {
|
|||
|
||||
public WandOfTheElementsFirstToken() {
|
||||
super("Elemental", "2/2 blue Elemental creature token with flying");
|
||||
setTokenType(1);
|
||||
cardType.add(CardType.CREATURE);
|
||||
this.subtype.add(SubType.ELEMENTAL);
|
||||
this.color.setBlue(true);
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
package mage.game.permanent.token;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.MageInt;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -39,6 +39,7 @@ public class WandOfTheElementsSecondToken extends Token {
|
|||
|
||||
public WandOfTheElementsSecondToken() {
|
||||
super("Elemental", "3/3 red Elemental creature token");
|
||||
setTokenType(2);
|
||||
cardType.add(CardType.CREATURE);
|
||||
this.subtype.add(SubType.ELEMENTAL);
|
||||
this.color.setRed(true);
|
||||
|
|
|
@ -1182,9 +1182,11 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
protected void restoreState(int bookmark, String text, Game game) {
|
||||
game.restoreState(bookmark, text);
|
||||
if (storedBookmark >= bookmark) {
|
||||
resetStoredBookmark(game);
|
||||
if (storedBookmark > -1) { // e.g. a turn rollback sets this to -1 so no more rollback of current action may be done
|
||||
game.restoreState(bookmark, text);
|
||||
if (storedBookmark >= bookmark) {
|
||||
resetStoredBookmark(game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2334,12 +2336,13 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
count = searchedLibrary.count(target.getFilter(), game);
|
||||
} else {
|
||||
Player targetPlayer = game.getPlayer(targetPlayerId);
|
||||
if (targetPlayer != null) {
|
||||
if (cardsFromTop == null) {
|
||||
cardsFromTop = new ArrayList<>(targetPlayer.getLibrary().getTopCards(game, librarySearchLimit));
|
||||
} else {
|
||||
cardsFromTop.retainAll(targetPlayer.getLibrary().getCards(game));
|
||||
}
|
||||
if (targetPlayer == null) {
|
||||
return false;
|
||||
}
|
||||
if (cardsFromTop == null) {
|
||||
cardsFromTop = new ArrayList<>(targetPlayer.getLibrary().getTopCards(game, librarySearchLimit));
|
||||
} else {
|
||||
cardsFromTop.retainAll(targetPlayer.getLibrary().getCards(game));
|
||||
}
|
||||
newTarget.setCardLimit(Math.min(librarySearchLimit, cardsFromTop.size()));
|
||||
count = Math.min(searchedLibrary.count(target.getFilter(), game), librarySearchLimit);
|
||||
|
@ -2390,7 +2393,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
chooseCard.setMessage("Which creature do you wish to cast from your library?");
|
||||
Set<String> choice = new LinkedHashSet<>();
|
||||
for (Entry<UUID, String> entry : libraryCastableCardTracker.entrySet()) {
|
||||
choice.add(new AbstractMap.SimpleEntry<UUID, String>(entry).getValue());
|
||||
choice.add(new AbstractMap.SimpleEntry<>(entry).getValue());
|
||||
}
|
||||
chooseCard.setChoices(choice);
|
||||
while (!choice.isEmpty()) {
|
||||
|
@ -2454,6 +2457,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
/**
|
||||
* @param game
|
||||
* @param appliedEffects
|
||||
* @param numSides Number of sides the dice has
|
||||
* @return the number that the player rolled
|
||||
*/
|
||||
@Override
|
||||
|
@ -3214,8 +3218,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setStoredBookmark(int storedBookmark
|
||||
) {
|
||||
public void setStoredBookmark(int storedBookmark) {
|
||||
this.storedBookmark = storedBookmark;
|
||||
}
|
||||
|
||||
|
@ -3802,4 +3805,11 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
return this.getId().equals(obj.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 89 * hash + Objects.hashCode(this.playerId);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue