Merge origin/master

This commit is contained in:
Styxo 2016-10-04 15:13:38 +02:00
commit 3c7c584bab
34 changed files with 823 additions and 351 deletions

View file

@ -1,32 +1,26 @@
package mage.client.cards;
import mage.cards.MageCard;
import mage.cards.decks.Deck;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.client.MageFrame;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLayout;
import mage.client.dialog.PreferencesDialog;
import mage.client.plugins.impl.Plugins;
import mage.client.util.*;
import mage.client.util.Event;
import mage.constants.CardType;
import mage.game.GameException;
import mage.view.CardView;
import mage.view.CardsView;
import org.apache.log4j.Logger;
import org.mage.card.arcane.CardRenderer;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Created by StravantUser on 2016-09-20.
@ -358,6 +352,15 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
public void setRole(Role role) {
this.role = role;
if (role == Role.SIDEBOARD) {
creatureCountLabel.setVisible(false);
landCountLabel.setVisible(false);
cardSizeSliderLabel.setVisible(false);
} else {
creatureCountLabel.setVisible(true);
landCountLabel.setVisible(true);
cardSizeSliderLabel.setVisible(true);
}
updateCounts();
}
@ -379,6 +382,23 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
cardContent.repaint();
}
public DeckCardLayout getCardLayout() {
// 2D Array to put entries into
List<List<List<DeckCardInfo>>> info = new ArrayList<>();
for (ArrayList<ArrayList<CardView>> gridRow : cardGrid) {
List<List<DeckCardInfo>> row = new ArrayList<>();
info.add(row);
for (ArrayList<CardView> stack : gridRow) {
row.add(stack.stream()
.map(card -> new DeckCardInfo(card.getName(), card.getCardNumber(), card.getExpansionSetCode()))
.collect(Collectors.toList()));
}
}
// Store layout and settings then return them
return new DeckCardLayout(info, saveSettings().toString());
}
public enum Sort {
NONE("No Sort", new Comparator<CardView>() {
@Override
@ -476,6 +496,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
JPopupMenu sortPopup;
JCheckBox separateCreaturesCb;
JSlider cardSizeSlider;
JLabel cardSizeSliderLabel;
Map<Sort, AbstractButton> sortButtons = new HashMap<>();
JLabel deckNameAndCountLabel;
JLabel landCountLabel;
JLabel creatureCountLabel;
@ -489,6 +514,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
// Card area selection panel
SelectionBox selectionPanel;
Set<CardView> selectionDragStartCards;
int selectionDragStartX;
int selectionDragStartY;
@ -526,6 +552,55 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
private String name;
}
public static class Settings {
public Sort sort;
public boolean separateCreatures;
private static Pattern parser = Pattern.compile("\\(([^,]*),([^)]*)\\)");
public static Settings parse(String str) {
Matcher m = parser.matcher(str);
if (m.find()) {
Settings s = new Settings();
s.sort = Sort.valueOf(m.group(1));
s.separateCreatures = Boolean.valueOf(m.group(2));
return s;
} else {
return null;
}
}
@Override
public String toString() {
return "(" + sort.toString() + "," + Boolean.toString(separateCreatures) + ")";
}
}
public Settings saveSettings() {
Settings s = new Settings();
s.sort = cardSort;
s.separateCreatures = separateCreatures;
return s;
}
public void loadSettings(Settings s) {
if (s != null) {
setSort(s.sort);
setSeparateCreatures(s.separateCreatures);
resort();
}
}
public void setSeparateCreatures(boolean state) {
separateCreatures = state;
separateCreaturesCb.setSelected(state);
}
public void setSort(Sort s) {
cardSort = s;
sortButtons.get(s).setSelected(true);
}
// Constructor
public DragCardGrid() {
// Make sure that the card grid is populated with at least one (empty) stack to begin with
@ -540,22 +615,6 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
filterButton = new JButton("Filter");
visibilityButton = new JButton("Visibility");
addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
deselectAll();
}
});
// Tmp load button
JButton loadButton = new JButton("Load");
loadButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
loadDeck();
}
});
// Name and count label
deckNameAndCountLabel = new JLabel();
@ -576,29 +635,26 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
toolbarInner.add(sortButton);
toolbarInner.add(filterButton);
toolbarInner.add(visibilityButton);
toolbarInner.add(loadButton);
toolbar.add(toolbarInner, BorderLayout.WEST);
JPanel sliderPanel = new JPanel(new GridBagLayout());
sliderPanel.setOpaque(false);
final JSlider sizeSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 50);
sizeSlider.setOpaque(false);
sizeSlider.setPreferredSize(new Dimension(100, (int)sizeSlider.getPreferredSize().getHeight()));
sizeSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (!sizeSlider.getValueIsAdjusting()) {
// Fraction in [-1, 1]
float sliderFrac = ((float) (sizeSlider.getValue() - 50)) / 50;
// Convert to frac in [0.5, 2.0] exponentially
cardSizeMod = (float) Math.pow(2, sliderFrac);
// Update grid
layoutGrid();
cardContent.repaint();
}
cardSizeSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 50);
cardSizeSlider.setOpaque(false);
cardSizeSlider.setPreferredSize(new Dimension(100, (int) cardSizeSlider.getPreferredSize().getHeight()));
cardSizeSlider.addChangeListener(e -> {
if (!cardSizeSlider.getValueIsAdjusting()) {
// Fraction in [-1, 1]
float sliderFrac = ((float) (cardSizeSlider.getValue() - 50)) / 50;
// Convert to frac in [0.5, 2.0] exponentially
cardSizeMod = (float) Math.pow(2, sliderFrac);
// Update grid
layoutGrid();
cardContent.repaint();
}
});
sliderPanel.add(new JLabel("Card Size:"));
sliderPanel.add(sizeSlider);
cardSizeSliderLabel = new JLabel("Card Size:");
sliderPanel.add(cardSizeSliderLabel);
sliderPanel.add(cardSizeSlider);
toolbar.add(sliderPanel, BorderLayout.EAST);
this.add(toolbar, BorderLayout.NORTH);
@ -610,9 +666,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
private boolean isDragging = false;
@Override
public void mousePressed(MouseEvent e) {
isDragging = true;
beginSelectionDrag(e.getX(), e.getY());
updateSelectionDrag(e.getX(), e.getY());
if (SwingUtilities.isLeftMouseButton(e)) {
isDragging = true;
beginSelectionDrag(e.getX(), e.getY(), e.isShiftDown());
updateSelectionDrag(e.getX(), e.getY());
}
}
@Override
public void mouseReleased(MouseEvent e) {
@ -673,15 +731,13 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
if (s == cardSort) {
button.setSelected(true);
}
sortButtons.put(s, button);
sortMode.add(button);
sortModeGroup.add(button);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
cardSort = s;
PreferencesDialog.saveValue(PreferencesDialog.KEY_DECK_EDITOR_LAST_SORT, s.toString());
resort();
}
button.addActionListener(e -> {
cardSort = s;
PreferencesDialog.saveValue(PreferencesDialog.KEY_DECK_EDITOR_LAST_SORT, s.toString());
resort();
});
}
@ -695,13 +751,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
separateCreaturesCb = new JCheckBox();
separateCreaturesCb.setText("Creatures in separate row");
separateCreaturesCb.setSelected(separateCreatures);
separateCreaturesCb.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
separateCreatures = separateCreaturesCb.isSelected();
PreferencesDialog.saveValue(PreferencesDialog.KEY_DECK_EDITOR_LAST_SEPARATE_CREATURES, Boolean.toString(separateCreatures));
resort();
}
separateCreaturesCb.addItemListener(e -> {
setSeparateCreatures(separateCreaturesCb.isSelected());
PreferencesDialog.saveValue(PreferencesDialog.KEY_DECK_EDITOR_LAST_SEPARATE_CREATURES, Boolean.toString(separateCreatures));
resort();
});
sortOptions.add(separateCreaturesCb);
@ -714,20 +767,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
{
final JPopupMenu visPopup = new JPopupMenu();
JMenuItem hideSelected = new JMenuItem("Hide selected");
hideSelected.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
hideSelection();
}
});
hideSelected.addActionListener(e -> hideSelection());
visPopup.add(hideSelected);
JMenuItem showAll = new JMenuItem("Show all");
showAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
showAll();
}
});
showAll.addActionListener(e -> showAll());
visPopup.add(showAll);
visibilityButton.addMouseListener(new MouseAdapter() {
@Override
@ -743,7 +786,6 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
makeButtonPopup(filterButton, filterPopup);
filterButton.setVisible(false);
loadButton.setVisible(false);
// Right click in card area
initCardAreaPopup();
@ -756,44 +798,29 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
final JPopupMenu menu = new JPopupMenu();
final JMenuItem hideSelected = new JMenuItem("Hide selected");
hideSelected.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
hideSelection();
}
});
hideSelected.addActionListener(e -> hideSelection());
menu.add(hideSelected);
JMenuItem showAll = new JMenuItem("Show all");
showAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
showAll();
}
});
showAll.addActionListener(e -> showAll());
menu.add(showAll);
JMenu sortMenu = new JMenu("Sort by...");
final Map<Sort, JMenuItem> sortMenuItems = new LinkedHashMap<>();
for (final Sort sort : Sort.values()) {
JMenuItem subSort = new JMenuItem(sort.getText());
subSort.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
cardSort = sort;
resort();
}
JMenuItem subSort = new JCheckBoxMenuItem(sort.getText());
sortMenuItems.put(sort, subSort);
subSort.addActionListener(e -> {
cardSort = sort;
resort();
});
sortMenu.add(subSort);
}
sortMenu.add(new JPopupMenu.Separator());
final JCheckBoxMenuItem separateButton = new JCheckBoxMenuItem("Separate creatures");
separateButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
separateCreatures = !separateCreatures;
separateCreaturesCb.setSelected(separateCreatures);
resort();
}
separateButton.addActionListener(e -> {
setSeparateCreatures(!separateCreatures);
resort();
});
sortMenu.add(separateButton);
menu.add(sortMenu);
@ -803,6 +830,9 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
for (Sort s : sortMenuItems.keySet()) {
sortMenuItems.get(s).setSelected(cardSort == s);
}
hideSelected.setEnabled(dragCardList().size() > 0);
separateButton.setSelected(separateCreatures);
menu.show(e.getComponent(), e.getX(), e.getY());
@ -843,7 +873,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
/**
* Selection drag handling
*/
private void beginSelectionDrag(int x, int y) {
private void beginSelectionDrag(int x, int y, boolean shiftHeld) {
// Show the selection panel
selectionPanel.setVisible(true);
selectionPanel.setLocation(x, y);
@ -853,6 +883,12 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
selectionDragStartX = x;
selectionDragStartY = y;
// Store the starting cards to include in the selection
selectionDragStartCards = new HashSet<>();
if (shiftHeld) {
selectionDragStartCards.addAll(dragCardList());
}
// Notify selection
notifyCardsSelected();
}
@ -904,7 +940,8 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
boolean inBoundsX = (col >= col1 && col <= col2);
boolean inBoundsY = (i >= stackStartIndex && i <= stackEndIndex);
boolean lastCard = (i == stack.size()-1);
if (inBoundsX && (inBoundsY || (lastCard && (y2 >= stackBottomBegin && y1 <= stackBottomEnd)))) {
boolean inSeletionDrag = inBoundsX && (inBoundsY || (lastCard && (y2 >= stackBottomBegin && y1 <= stackBottomEnd)));
if (inSeletionDrag || selectionDragStartCards.contains(card)) {
if (!card.isSelected()) {
card.setSelected(true);
view.update(card);
@ -921,42 +958,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
}
}
private void endSelectionDrag(int x, int y) {
private void endSelectionDrag(@SuppressWarnings("unused") int x, @SuppressWarnings("unused") int y) {
// Hide the selection panel
selectionPanel.setVisible(false);
}
private void loadDeck() {
JFileChooser fcSelectDeck = new JFileChooser();
String lastFolder = MageFrame.getPreferences().get("lastDeckFolder", "");
if (!lastFolder.isEmpty()) {
fcSelectDeck.setCurrentDirectory(new File(lastFolder));
}
int ret = fcSelectDeck.showOpenDialog(DragCardGrid.this);
if (ret == JFileChooser.APPROVE_OPTION) {
File file = fcSelectDeck.getSelectedFile();
try {
setCursor(new Cursor(Cursor.WAIT_CURSOR));
Deck deck = Deck.load(DeckImporterUtil.importDeck(file.getPath()), true, true);
setCards(new CardsView(deck.getCards()), null);
} catch (GameException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
try {
if (file != null) {
MageFrame.getPreferences().put("lastDeckFolder", file.getCanonicalPath());
}
} catch (IOException ex) {
}
}
fcSelectDeck.setSelectedFile(null);
}
// Resort the existing cards based on the current sort
public void resort() {
// First null out the grid and trim it down
@ -984,7 +990,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
}
// Update the contents of the card grid
public void setCards(CardsView cardsView, BigCard bigCard) {
public void setCards(CardsView cardsView, DeckCardLayout layout, BigCard bigCard) {
if (bigCard != null) {
lastBigCard = bigCard;
}
@ -1014,20 +1020,89 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
trimGrid();
}
// Add any new card views
for (CardView newCard: cardsView.values()) {
if (!cardViews.containsKey(newCard.getId())) {
// Is a new card
addCardView(newCard);
if (layout == null) {
// No layout -> add any new card views one at a time as par the current sort
for (CardView newCard: cardsView.values()) {
if (!cardViews.containsKey(newCard.getId())) {
// Is a new card
addCardView(newCard);
try {
// Put it into the appropirate place in the grid given the current sort
sortIntoGrid(newCard);
} catch (Exception e) {
e.printStackTrace();
// Mark
didModify = true;
}
}
} else {
// Layout given -> Build card grid using layout, and set sort / separate
// Always modify when given a layout
didModify = true;
// Load in settings
loadSettings(Settings.parse(layout.getSettings()));
// Traverse the cards once and track them so we can pick ones to insert into the grid
Map<String, Map<String, ArrayList<CardView>>> trackedCards = new HashMap<>();
for (CardView newCard: cardsView.values()) {
if (!cardViews.containsKey(newCard.getId())) {
// Add the new card
addCardView(newCard);
// Add the new card to tracking
Map<String, ArrayList<CardView>> forSetCode;
if (trackedCards.containsKey(newCard.getExpansionSetCode())) {
forSetCode = trackedCards.get(newCard.getExpansionSetCode());
} else {
forSetCode = new HashMap<>();
trackedCards.put(newCard.getExpansionSetCode(), forSetCode);
}
ArrayList<CardView> list;
if (forSetCode.containsKey(newCard.getCardNumber())) {
list = forSetCode.get(newCard.getCardNumber());
} else {
list = new ArrayList<>();
forSetCode.put(newCard.getCardNumber(), list);
}
list.add(newCard);
}
}
// Now go through the layout and use it to build the cardGrid
cardGrid = new ArrayList<>();
maxStackSize = new ArrayList<>();
for (List<List<DeckCardInfo>> row : layout.getCards()) {
ArrayList<ArrayList<CardView>> gridRow = new ArrayList<>();
int thisMaxStackSize = 0;
cardGrid.add(gridRow);
for (List<DeckCardInfo> stack : row) {
ArrayList<CardView> gridStack = new ArrayList<>();
gridRow.add(gridStack);
for (DeckCardInfo info : stack) {
if (trackedCards.containsKey(info.getSetCode()) && trackedCards.get(info.getSetCode()).containsKey(info.getCardNum())) {
ArrayList<CardView> candidates =
trackedCards.get(info.getSetCode()).get(info.getCardNum());
if (candidates.size() > 0) {
gridStack.add(candidates.remove(0));
thisMaxStackSize = Math.max(thisMaxStackSize, gridStack.size());
}
}
}
}
maxStackSize.add(thisMaxStackSize);
}
// Check that there aren't any "orphans" not referenced in the layout. There should
// never be any under normal operation, but as a failsafe in case the user screwed with
// the file in an invalid way, sort them into the grid so that they aren't just left hanging.
for (Map<String, ArrayList<CardView>> tracked : trackedCards.values()) {
for (ArrayList<CardView> orphans : tracked.values()) {
for (CardView orphan : orphans) {
LOGGER.info("Orphan when setting with layout: ");
sortIntoGrid(orphan);
}
}
// Mark
didModify = true;
}
}
@ -1048,18 +1123,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
landCountLabel.setText("" + landCounter.get());
}
private void showCardRightClickMenu(final CardView card, MouseEvent e) {
private void showCardRightClickMenu(@SuppressWarnings("unused") final CardView card, MouseEvent e) {
JPopupMenu menu = new JPopupMenu();
JMenuItem hide = new JMenuItem("Hide");
hide.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//if (card.isSelected() && dragCardList().size() > 1) {
// Hide all selected
hideSelection();
//}
}
});
hide.addActionListener(e2 -> hideSelection());
menu.add(hide);
menu.show(e.getComponent(), e.getX(), e.getY());
}
@ -1074,7 +1141,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
updateCounts();
// Create the card view
final MageCard cardPanel = Plugins.getInstance().getMageCard(card, lastBigCard, new Dimension(100, 140), null, true, true);
final MageCard cardPanel = Plugins.getInstance().getMageCard(card, lastBigCard, new Dimension(getCardWidth(), getCardHeight()), null, true, true);
cardPanel.update(card);
cardPanel.setTextOffset(0);
@ -1156,8 +1223,17 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
}
}
private void toggleSelected(CardView targetCard) {
targetCard.setSelected(!targetCard.isSelected());
cardViews.get(targetCard.getId()).update(targetCard);
}
private void cardClicked(CardView targetCard, MouseEvent e) {
selectCard(targetCard);
if (e.isShiftDown()) {
toggleSelected(targetCard);
} else {
selectCard(targetCard);
}
notifyCardsSelected();
}
@ -1176,12 +1252,12 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
/**
* Add a card to the cardGrid, in the position that the current sort dictates
* @param newCard
* @param newCard Card to add to the cardGrid array.
*/
private void sortIntoGrid(CardView newCard) {
// Ensure row 1 exists
if (cardGrid.size() == 0) {
cardGrid.add(0, new ArrayList<ArrayList<CardView>>());
cardGrid.add(0, new ArrayList<>());
maxStackSize.add(0, 0);
}
// What row to add it to?
@ -1189,11 +1265,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
if (separateCreatures && !newCard.getCardTypes().contains(CardType.CREATURE)) {
// Ensure row 2 exists
if (cardGrid.size() < 2) {
cardGrid.add(1, new ArrayList<ArrayList<CardView>>());
cardGrid.add(1, new ArrayList<>());
maxStackSize.add(1, 0);
// Populate with stacks matching the first row
for (int i = 0; i < cardGrid.get(0).size(); ++i) {
cardGrid.get(1).add(new ArrayList<CardView>());
cardGrid.get(1).add(new ArrayList<>());
}
}
targetRow = cardGrid.get(1);
@ -1223,7 +1299,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
// Insert into this col, but if less, then we need to create a new col here first
if (res < 0) {
for (int rowIndex = 0; rowIndex < cardGrid.size(); ++rowIndex) {
cardGrid.get(rowIndex).add(currentColumn, new ArrayList<CardView>());
cardGrid.get(rowIndex).add(currentColumn, new ArrayList<>());
}
}
targetRow.get(currentColumn).add(newCard);
@ -1238,7 +1314,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
// If nothing else, insert in a new column after everything else
if (!didInsert) {
for (int rowIndex = 0; rowIndex < cardGrid.size(); ++rowIndex) {
cardGrid.get(rowIndex).add(new ArrayList<CardView>());
cardGrid.get(rowIndex).add(new ArrayList<>());
}
targetRow.get(targetRow.size()-1).add(newCard);
}
@ -1335,7 +1411,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
// Stack count label
if (stackCountLabels.size() <= rowIndex) {
stackCountLabels.add(new ArrayList<JLabel>());
stackCountLabels.add(new ArrayList<>());
}
if (stackCountLabels.get(rowIndex).size() <= colIndex) {
JLabel countLabel = new JLabel("", SwingConstants.CENTER);
@ -1379,70 +1455,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
}
private static void makeButtonPopup(final AbstractButton button, final JPopupMenu popup) {
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
popup.show(button, 0, button.getHeight());
}
});
}
static class DeckFilter extends FileFilter {
@Override
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
String ext = null;
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1) {
ext = s.substring(i + 1).toLowerCase();
}
return (ext == null) ? false : ext.equals("dck");
}
@Override
public String getDescription() {
return "Deck Files";
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
/*
GUISizeHelper.calculateGUISizes();
Plugins.getInstance().loadPlugins();
frame.setTitle("Test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setBackground(Color.BLUE);
DragCardGrid grid = new DragCardGrid();
grid.setPreferredSize(new Dimension(800, 600));
*/
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (UnsupportedLookAndFeelException | ClassNotFoundException | IllegalAccessException | InstantiationException e) {}
frame.setVisible(true);
JFileChooser choose = new JFileChooser();
choose.setAcceptAllFileFilterUsed(false);
choose.addChoosableFileFilter(new DeckFilter());
choose.showOpenDialog(frame);
LOGGER.info("File: " + choose.getSelectedFile());
String st = "";
try {
st = choose.getSelectedFile().getCanonicalPath();
} catch (Exception e) {
e.printStackTrace();
}
LOGGER.info("Selected file: " + st);
choose.setSelectedFile(new File(st));
choose.showOpenDialog(frame);
LOGGER.info("File: " + choose.getSelectedFile());
//frame.add(grid, BorderLayout.CENTER);
//frame.pack();
frame.setVisible(false);
button.addActionListener(e -> popup.show(button, 0, button.getHeight()));
}
}

View file

@ -34,6 +34,9 @@ package mage.client.deckeditor;
import mage.cards.Card;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLayout;
import mage.cards.decks.DeckCardLists;
import mage.client.cards.BigCard;
import mage.client.cards.CardEventSource;
import mage.client.cards.DragCardGrid;
@ -43,9 +46,12 @@ import mage.client.util.GUISizeHelper;
import mage.client.util.Listener;
import mage.view.CardView;
import mage.view.CardsView;
import org.apache.log4j.Logger;
import javax.swing.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
@ -58,6 +64,45 @@ public class DeckArea extends javax.swing.JPanel {
private Set<UUID> hiddenCards = new HashSet<>();
private Deck lastDeck = new Deck();
private BigCard lastBigCard = null;
private int dividerLocationNormal = 0;
private int dividerLocationLimited = 0;
private boolean isLimitedBuildingOrientation = false;
public DeckCardLayout getCardLayout() {
return deckList.getCardLayout();
}
public DeckCardLayout getSideboardLayout() {
return sideboardList.getCardLayout();
}
public static class Settings {
public DragCardGrid.Settings maindeckSettings;
public DragCardGrid.Settings sideboardSetings;
public int dividerLocationLimited;
public int dividerLocationNormal;
private static Pattern parser = Pattern.compile("([^|]*)\\|([^|]*)\\|([^|]*)\\|([^|]*)");
public static Settings parse(String s) {
Matcher m = parser.matcher(s);
if (m.find()) {
Settings settings = new Settings();
settings.maindeckSettings = DragCardGrid.Settings.parse(m.group(1));
settings.sideboardSetings = DragCardGrid.Settings.parse(m.group(2));
settings.dividerLocationNormal = Integer.parseInt(m.group(3));
settings.dividerLocationLimited = Integer.parseInt(m.group(4));
return settings;
} else {
return null;
}
}
@Override
public String toString() {
return maindeckSettings.toString() + "|" + sideboardSetings.toString() + "|" + dividerLocationNormal + "|" + dividerLocationLimited;
}
}
/**
* Creates new form DeckArea
@ -117,6 +162,38 @@ public class DeckArea extends javax.swing.JPanel {
});
}
public Settings saveSettings() {
Settings settings = new Settings();
settings.maindeckSettings = deckList.saveSettings();
settings.sideboardSetings = sideboardList.saveSettings();
if (isLimitedBuildingOrientation) {
dividerLocationLimited = deckAreaSplitPane.getDividerLocation();
} else {
dividerLocationNormal = deckAreaSplitPane.getDividerLocation();
}
settings.dividerLocationLimited = dividerLocationLimited;
settings.dividerLocationNormal = dividerLocationNormal;
return settings;
}
public void loadSettings(Settings s) {
if (s != null) {
deckList.loadSettings(s.maindeckSettings);
sideboardList.loadSettings(s.sideboardSetings);
dividerLocationLimited = s.dividerLocationLimited;
dividerLocationNormal = s.dividerLocationNormal;
if (isLimitedBuildingOrientation) {
if (dividerLocationLimited != 0) {
deckAreaSplitPane.setDividerLocation(s.dividerLocationLimited);
}
} else {
if (dividerLocationNormal != 0) {
deckAreaSplitPane.setDividerLocation(s.dividerLocationNormal);
}
}
}
}
public void cleanUp() {
deckList.cleanUp();
sideboardList.cleanUp();
@ -135,8 +212,14 @@ public class DeckArea extends javax.swing.JPanel {
public void setOrientation(boolean limitedBuildingOrientation) {
if (limitedBuildingOrientation) {
deckAreaSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
if (dividerLocationLimited != 0) {
deckAreaSplitPane.setDividerLocation(dividerLocationLimited);
}
} else {
deckAreaSplitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
if (dividerLocationNormal != 0) {
deckAreaSplitPane.setDividerLocation(dividerLocationNormal);
}
}
}
@ -161,11 +244,21 @@ public class DeckArea extends javax.swing.JPanel {
}
public void loadDeck(Deck deck, BigCard bigCard) {
loadDeck(deck, false, bigCard);
}
public void loadDeck(Deck deck, boolean useLayout, BigCard bigCard) {
lastDeck = deck;
lastBigCard = bigCard;
deckList.setCards(new CardsView(filterHidden(lastDeck.getCards())), lastBigCard);
deckList.setCards(
new CardsView(filterHidden(lastDeck.getCards())),
useLayout ? deck.getCardsLayout() : null,
lastBigCard);
if (sideboardList.isVisible()) {
sideboardList.setCards(new CardsView(filterHidden(lastDeck.getSideboard())), lastBigCard);
sideboardList.setCards(
new CardsView(filterHidden(lastDeck.getSideboard())),
useLayout ? deck.getSideboardLayout() : null,
lastBigCard);
}
}

View file

@ -28,10 +28,7 @@
package mage.client.deckeditor;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -50,6 +47,7 @@ import javax.swing.filechooser.FileFilter;
import mage.cards.Card;
import mage.cards.Sets;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardLists;
import mage.cards.decks.importer.DeckImporter;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.cards.repository.CardInfo;
@ -62,6 +60,7 @@ import mage.client.constants.Constants.DeckEditorMode;
import mage.client.deck.generator.DeckGenerator.DeckGeneratorException;
import mage.client.deck.generator.DeckGenerator;
import mage.client.dialog.AddLandDialog;
import mage.client.dialog.PreferencesDialog;
import mage.client.plugins.impl.Plugins;
import mage.client.util.Event;
import mage.client.util.Listener;
@ -106,12 +105,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
deckArea.setOpaque(false);
jPanel1.setOpaque(false);
jSplitPane1.setOpaque(false);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
jSplitPane1.setDividerLocation(0.3);
}
});
restoreDividerLocationsAndDeckAreaSettings();
countdown = new Timer(1000,
new ActionListener() {
@Override
@ -128,12 +122,22 @@ public class DeckEditorPanel extends javax.swing.JPanel {
}
}
});
// Set up tracking to save the deck editor settings when the deck editor is hidden.
addHierarchyListener((HierarchyEvent e) -> {
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
if (!isShowing()) {
saveDividerLocationsAndDeckAreaSettings();
}
}
});
}
/**
* Free resources so GC can remove unused objects from memory
*/
public void cleanUp() {
saveDividerLocationsAndDeckAreaSettings();
if (updateDeckTask != null) {
updateDeckTask.cancel(true);
}
@ -152,6 +156,24 @@ public class DeckEditorPanel extends javax.swing.JPanel {
this.bigCard = null;
}
private void saveDividerLocationsAndDeckAreaSettings() {
PreferencesDialog.saveValue(PreferencesDialog.KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION, Integer.toString(jSplitPane1.getDividerLocation()));
PreferencesDialog.saveValue(PreferencesDialog.KEY_EDITOR_DECKAREA_SETTINGS, this.deckArea.saveSettings().toString());
}
private void restoreDividerLocationsAndDeckAreaSettings() {
// Load horizontal split position setting
String dividerLocation = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION, "");
if (!dividerLocation.isEmpty()) {
jSplitPane1.setDividerLocation(Integer.parseInt(dividerLocation));
}
// Load deck area settings
this.deckArea.loadSettings(
DeckArea.Settings.parse(
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_EDITOR_DECKAREA_SETTINGS, "")));
}
public void changeGUISize() {
this.cardSelector.changeGUISize();
this.deckArea.changeGUISize();
@ -556,10 +578,14 @@ public class DeckEditorPanel extends javax.swing.JPanel {
}
private void refreshDeck() {
refreshDeck(false);
}
private void refreshDeck(boolean useLayout) {
try {
setCursor(new Cursor(Cursor.WAIT_CURSOR));
this.txtDeckName.setText(deck.getName());
deckArea.loadDeck(deck, bigCard);
deckArea.loadDeck(deck, useLayout, bigCard);
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
@ -615,13 +641,6 @@ public class DeckEditorPanel extends javax.swing.JPanel {
jSplitPane1.setTopComponent(cardSelector);
jSplitPane1.setBottomComponent(deckArea);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
jSplitPane1.setDividerLocation(0.6);
}
});
bigCard.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
cardInfoPane = Plugins.getInstance().getCardInfoPane();
@ -878,7 +897,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
refreshDeck();
refreshDeck(true);
try {
if (file != null) {
MageFrame.getPreferences().put("lastDeckFolder", file.getCanonicalPath());
@ -919,7 +938,10 @@ public class DeckEditorPanel extends javax.swing.JPanel {
fileName += ".dck";
}
setCursor(new Cursor(Cursor.WAIT_CURSOR));
Sets.saveDeck(fileName, deck.getDeckCardLists());
DeckCardLists cardLists = deck.getDeckCardLists();
cardLists.setCardLayout(deckArea.getCardLayout());
cardLists.setSideboardLayout(deckArea.getSideboardLayout());
Sets.saveDeck(fileName, cardLists);
} catch (FileNotFoundException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage() + "\nTry ensuring that the selected directory is writable.", "Error saving deck", JOptionPane.ERROR_MESSAGE);
} finally {

View file

@ -185,6 +185,10 @@ public class PreferencesDialog extends javax.swing.JDialog {
public static final String KEY_TABLES_DIVIDER_LOCATION_2 = "tablePanelDividerLocation2";
public static final String KEY_TABLES_DIVIDER_LOCATION_3 = "tablePanelDividerLocation3";
// Positions of deck editor divider bars
public static final String KEY_EDITOR_HORIZONTAL_DIVIDER_LOCATION = "editorHorizontalDividerLocation";
public static final String KEY_EDITOR_DECKAREA_SETTINGS = "editorDeckAreaSettings";
// user list
public static final String KEY_USERS_COLUMNS_WIDTH = "userPanelColumnWidth";
public static final String KEY_USERS_COLUMNS_ORDER = "userPanelColumnSort";

View file

@ -602,7 +602,7 @@ public class TablesPanel extends javax.swing.JPanel {
formatFilterList.add(RowFilter.regexFilter("^Limited", TableTableModel.COLUMN_DECK_TYPE));
}
if (btnFormatOther.isSelected()) {
formatFilterList.add(RowFilter.regexFilter("^Momir Basic|^Constructed - Pauper|^Constructed - Frontier|^Constructed - Extended|^Constructed - Historical|^Constructed - Super|^Constructed - Freeform", TableTableModel.COLUMN_DECK_TYPE));
formatFilterList.add(RowFilter.regexFilter("^Momir Basic|^Constructed - Pauper|^Constructed - Frontier|^Constructed - Extended|^Constructed - Eternal|^Constructed - Historical|^Constructed - Super|^Constructed - Freeform", TableTableModel.COLUMN_DECK_TYPE));
}
List<RowFilter<Object, Object>> skillFilterList = new ArrayList<>();

View file

@ -25,6 +25,10 @@ public class ConstructedFormats {
public static final String EXTENDED = "- Extended";
public static final String FRONTIER = "- Frontier";
public static final String MODERN = "- Modern";
public static final String VINTAGE_LEGACY = "- Vintage / Legacy";
;
public static final String CUSTOM = "- Custom";
;
public static final Standard STANDARD_CARDS = new Standard();
private static final Map<String, List<String>> underlyingSetCodesPerFormat = new HashMap<>();
@ -50,50 +54,49 @@ public class ConstructedFormats {
}
public static void ensureLists() {
if (getSetsByFormat(ConstructedFormats.STANDARD) == null) {
if (underlyingSetCodesPerFormat.isEmpty()) {
buildLists();
}
}
private static void buildLists() {
underlyingSetCodesPerFormat.put(STANDARD, new ArrayList<>());
underlyingSetCodesPerFormat.put(EXTENDED, new ArrayList<>());
underlyingSetCodesPerFormat.put(FRONTIER, new ArrayList<>());
underlyingSetCodesPerFormat.put(MODERN, new ArrayList<>());
underlyingSetCodesPerFormat.put(VINTAGE_LEGACY, new ArrayList<>());
underlyingSetCodesPerFormat.put(CUSTOM, new ArrayList<>());
final Map<String, ExpansionInfo> expansionInfo = new HashMap<>();
formats.clear(); // prevent NPE on sorting if this is not the first try
for (ExpansionInfo set : ExpansionRepository.instance.getAll()) {
expansionInfo.put(set.getName(), set);
formats.add(set.getName());
underlyingSetCodesPerFormat.put(set.getName(), new ArrayList<>());
underlyingSetCodesPerFormat.get(set.getName()).add(set.getCode());
// create the play formats
if (set.getType().equals(SetType.CUSTOM_SET)) {
underlyingSetCodesPerFormat.get(CUSTOM).add(set.getCode());
continue;
}
underlyingSetCodesPerFormat.get(VINTAGE_LEGACY).add(set.getCode());
if (set.getType().equals(SetType.CORE) || set.getType().equals(SetType.EXPANSION) || set.getType().equals(SetType.SUPPLEMENTAL_STANDARD_LEGAL)) {
if (STANDARD_CARDS.getSetCodes().contains(set.getCode())) {
if (underlyingSetCodesPerFormat.get(STANDARD) == null) {
underlyingSetCodesPerFormat.put(STANDARD, new ArrayList<>());
}
underlyingSetCodesPerFormat.get(STANDARD).add(set.getCode());
}
if (set.getReleaseDate().after(extendedDate)) {
if (underlyingSetCodesPerFormat.get(EXTENDED) == null) {
underlyingSetCodesPerFormat.put(EXTENDED, new ArrayList<>());
}
underlyingSetCodesPerFormat.get(EXTENDED).add(set.getCode());
}
if (set.getReleaseDate().after(frontierDate)) {
if (underlyingSetCodesPerFormat.get(FRONTIER) == null) {
underlyingSetCodesPerFormat.put(FRONTIER, new ArrayList<>());
}
underlyingSetCodesPerFormat.get(FRONTIER).add(set.getCode());
}
if (set.getReleaseDate().after(modernDate)) {
if (underlyingSetCodesPerFormat.get(MODERN) == null) {
underlyingSetCodesPerFormat.put(MODERN, new ArrayList<>());
}
underlyingSetCodesPerFormat.get(MODERN).add(set.getCode());
}
}
if (underlyingSetCodesPerFormat.get(set.getName()) == null) {
underlyingSetCodesPerFormat.put(set.getName(), new ArrayList<>());
}
underlyingSetCodesPerFormat.get(set.getName()).add(set.getCode());
// Create the Block formats
if (set.getType().equals(SetType.EXPANSION) && set.getBlockName() != null) {
String blockDisplayName = getBlockDisplayName(set.getBlockName());
if (underlyingSetCodesPerFormat.get(blockDisplayName) == null) {
@ -209,10 +212,13 @@ public class ConstructedFormats {
});
if (!formats.isEmpty()) {
formats.add(0, CUSTOM);
formats.add(0, VINTAGE_LEGACY);
formats.add(0, MODERN);
formats.add(0, FRONTIER);
formats.add(0, EXTENDED);
formats.add(0, FRONTIER);
formats.add(0, STANDARD);
}
formats.add(0, ALL);
}

View file

@ -153,11 +153,13 @@ public class ManaSymbols {
sizedSymbols.put(symbol, notResized);
} else {
Rectangle r = new Rectangle(size, size);
Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
BufferedImage resized = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r);
//Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
BufferedImage image = ImageIO.read(file);
//BufferedImage resized = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r);
BufferedImage resized = ImageHelper.getResizedImage(image, r);
sizedSymbols.put(symbol, resized);
}
} catch (Exception e) {
} catch (IOException e) {
LOGGER.error("Error for symbol:" + symbol);
fileErrors = true;
}

View file

@ -73,6 +73,28 @@ public class GathererSymbols implements Iterable<DownloadJob> {
String symbol = sym.replaceAll("/", "");
File dst = new File(dir, symbol + ".gif");
/**
* Handle a bug on Gatherer where a few symbols are missing at the large size.
* Fall back to using the medium symbol for those cases.
*/
int modSizeIndex = sizeIndex;
if (sizeIndex == 2) {
switch (sym) {
case "WP":
case "UP":
case "BP":
case "RP":
case "GP":
case "E":
case "C":
modSizeIndex = 1;
break;
default:
// Nothing to do, symbol is available in the large size
}
}
switch (symbol) {
case "T":
symbol = "tap";
@ -85,7 +107,7 @@ public class GathererSymbols implements Iterable<DownloadJob> {
break;
}
String url = format(urlFmt, sizes[sizeIndex], symbol);
String url = format(urlFmt, sizes[modSizeIndex], symbol);
return new DownloadJob(sym, fromURL(url), toFile(dst));
}

View file

@ -177,6 +177,7 @@ public class MtgOnlTokensImageSource implements CardImageSource {
copyUrlToImage.put("Elemental_BR_5_5.jpg", "ELEMENTAL.BR.ELEMENTAL.CREATURE.1.1.full.jpg");
copyUrlToImage.put("Elemental_GW_y_y.jpg", "ELEMENTAL.WG.ELEMENTAL.CREATURE.S.S.full.jpg");
copyUrlToImage.put("Elemental_G_2_2.jpg", "ELEMENTAL.G.ELEMENTAL.CREATURE.2.2.full.jpg");
copyUrlToImage.put("Elemental_R_3_1.jpg", "ELEMENTAL.R.ELEMENTAL.CREATURE.3.1.full.jpg");
copyUrlToImage.put("Elemental_G_4_4.jpg", "ELEMENTAL.G.ELEMENTAL.CREATURE.4.4.full.jpg");
copyUrlToImage.put("Elemental_G_5_3.jpg", "ELEMENTAL.G.ELEMENTAL.CREATURE.5.3.full.jpg");
copyUrlToImage.put("Elemental_G_7_7.jpg", "ELEMENTAL.G.ELEMENTAL.CREATURE.7.7.full.jpg");

View file

@ -24,8 +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.view;
import java.io.Serializable;
@ -40,12 +39,13 @@ import mage.abilities.Ability;
* @author BetaSteward_at_googlemail.com
*/
public class AbilityPickerView implements Serializable {
private static final long serialVersionUID = 1L;
private Map<UUID, String> choices = new LinkedHashMap<UUID, String>();
private Map<UUID, String> choices = new LinkedHashMap<>();
public AbilityPickerView(String objectName, List<? extends Ability> abilities) {
for (Ability ability: abilities) {
for (Ability ability : abilities) {
if (objectName == null) {
choices.put(ability.getId(), ability.getRule(true));
} else {

View file

@ -324,7 +324,7 @@ public class CardView extends SimpleCardView {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
Mode mode = spellAbility.getModes().get(modeId);
if (mode.getTargets().size() > 0) {
setTargets(spellAbility.getTargets());
setTargets(mode.getTargets());
}
}
}

View file

@ -125,7 +125,7 @@ public class PlayerView implements Serializable {
} catch (ConcurrentModificationException e) {
// can happen as a player left battlefield while PlayerView is created
}
this.topCard = player.isTopCardRevealed() && player.getLibrary().size() > 0
this.topCard = (player.isTopCardRevealed() && player.getLibrary().size() > 0)
? new CardView(player.getLibrary().getFromTop(game)) : null;
if (player.getUserData() != null) {
this.userData = player.getUserData();

View file

@ -24,11 +24,13 @@
* 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.deck;
import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.decks.Constructed;
import mage.constants.SetType;
/**
*
@ -38,7 +40,11 @@ public class Legacy extends Constructed {
public Legacy() {
super("Constructed - Legacy");
for (ExpansionSet set : Sets.getInstance().values()) {
if (set.getSetType() != SetType.CUSTOM_SET) {
setCodes.add(set.getCode());
}
}
banned.add("Advantageous Proclamation");
banned.add("Amulet of Quoz");
banned.add("Ancestral Recall");
@ -112,6 +118,6 @@ public class Legacy extends Constructed {
banned.add("Worldknit");
banned.add("Yawgmoth's Bargain");
banned.add("Yawgmoth's Will");
}
}

View file

@ -27,7 +27,10 @@
*/
package mage.deck;
import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.decks.Constructed;
import mage.constants.SetType;
/**
*
@ -37,7 +40,11 @@ public class Vintage extends Constructed {
public Vintage() {
super("Constructed - Vintage");
for (ExpansionSet set : Sets.getInstance().values()) {
if (set.getSetType() != SetType.CUSTOM_SET) {
setCodes.add(set.getCode());
}
}
banned.add("Advantageous Proclamation");
banned.add("Amulet of Quoz");
banned.add("Backup Plan");

View file

@ -1,16 +1,16 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
@ -20,7 +20,7 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* 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.
@ -47,7 +47,7 @@ public class CommanderDuelMatch extends MatchImpl {
boolean alsoHand = true;
// Don't like it to compare but seems like it's complicated to do it in another way
if (options.getDeckType().equals("Variant Magic - Duel Commander")) {
startLife = 30;
startLife = 20; // Starting with the Commander 2016 update (on November 11th, 2016), Duel Commander will be played with 20 life points instead of 30.
alsoHand = true; // commander going to hand allowed to go to command zone effective July 17, 2015
}
CommanderDuel game = new CommanderDuel(options.getAttackOption(), options.getRange(), options.getFreeMulligans(), startLife);

View file

@ -284,7 +284,7 @@ public class Session {
lockSet = true;
logger.debug("SESSION LOCK SET sessionId: " + sessionId);
} else {
logger.error("CAN'T GET LOCK - userId: " + userId);
logger.error("CAN'T GET LOCK - userId: " + userId + " hold count: " + lock.getHoldCount());
}
User user = UserManager.getInstance().getUser(userId);
if (user == null || !user.isConnected()) {

View file

@ -109,8 +109,8 @@ class ThievesAuctionEffect extends OneShotEffect {
// Starting with you, each player
PlayerList playerList = game.getState().getPlayersInRange(controller.getId(), game);
Player player = playerList.getCurrent(game);
while (!exiledCards.isEmpty()) {
if (player.canRespond()) {
while (!exiledCards.isEmpty() && !game.hasEnded()) {
if (player != null && player.canRespond()) {
// chooses one of the exiled cards
TargetCard target = new TargetCardInExile(new FilterCard());
if (player.choose(Outcome.PutCardInPlay, exiledCards, target, game)) {

View file

@ -105,11 +105,13 @@ class AvatarOfMightCostReductionEffect extends CostModificationEffectImpl {
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
int creatures = game.getBattlefield().countAll(new FilterCreaturePermanent(), source.getControllerId(), game);
for (UUID playerId : game.getOpponents(source.getControllerId())) {
Player opponent = game.getPlayer(playerId);
if (opponent != null && game.getBattlefield().countAll(new FilterCreaturePermanent(), opponent.getId(), game) >= creatures + 4) {
return true;
if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) {
int creatures = game.getBattlefield().countAll(new FilterCreaturePermanent(), source.getControllerId(), game);
for (UUID playerId : game.getOpponents(source.getControllerId())) {
Player opponent = game.getPlayer(playerId);
if (opponent != null && game.getBattlefield().countAll(new FilterCreaturePermanent(), opponent.getId(), game) >= creatures + 4) {
return true;
}
}
}
return false;

View file

@ -0,0 +1,99 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* 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 org.mage.test.cards.modal;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class SameModeMoreThanOnceTest extends CardTestPlayerBase {
@Test
public void testEachModeOnce() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
// Choose three. You may choose the same mode more than once.
// - Target player draws a card and loses 1 life;
// - Target creature gets -2/-2 until end of turn;
// - Return target creature card from your graveyard to your hand.
addCard(Zone.HAND, playerA, "Wretched Confluence"); // Instant {3}{B}{B}
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wretched Confluence", "mode=1targetPlayer=PlayerA^mode=2Pillarfield Ox^mode=3Silvercoat Lion");
setModeChoice(playerA, "1");
setModeChoice(playerA, "2");
setModeChoice(playerA, "3");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Wretched Confluence", 1);
assertLife(playerA, 19);
assertLife(playerB, 20);
assertHandCount(playerA, 2);
assertPowerToughness(playerB, "Pillarfield Ox", 0, 2);
assertGraveyardCount(playerA, "Silvercoat Lion", 0);
}
@Test
public void testSecondModeTwiceThridModeOnce() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
// Choose three. You may choose the same mode more than once.
// - Target player draws a card and loses 1 life;
// - Target creature gets -2/-2 until end of turn;
// - Return target creature card from your graveyard to your hand.
addCard(Zone.HAND, playerA, "Wretched Confluence"); // Instant {3}{B}{B}
addCard(Zone.BATTLEFIELD, playerB, "Wall of Air");
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox");
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wretched Confluence", "mode=1Pillarfield Ox^mode=2Wall of Air^mode=3Silvercoat Lion");
setModeChoice(playerA, "2");
setModeChoice(playerA, "2");
setModeChoice(playerA, "3");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Wretched Confluence", 1);
assertLife(playerA, 20);
assertLife(playerB, 20);
assertPowerToughness(playerB, "Wall of Air", -1, 3);
assertPowerToughness(playerB, "Pillarfield Ox", 0, 2);
assertGraveyardCount(playerA, "Silvercoat Lion", 0);
}
}

View file

@ -35,7 +35,9 @@ public class DenseFoliageTest extends CardTestPlayerBase {
*/
@Test
public void testAbilityCanTarget() {
// Creatures can't be the targets of spells
addCard(Zone.BATTLEFIELD, playerA, "Dense Foliage");
//{T}: Prodigal Sorcerer deals 1 damage to target creature or player.
addCard(Zone.BATTLEFIELD, playerA, "Prodigal Sorcerer");
addCard(Zone.BATTLEFIELD, playerB, "Eager Cadet");

View file

@ -282,19 +282,14 @@ public class TestPlayer implements Player {
Mode selectedMode = null;
if (targetName.startsWith("mode=")) {
int modeNr = Integer.parseInt(targetName.substring(5, 6));
if (modeNr == 0 || modeNr > ability.getModes().size()) {
if (modeNr == 0 || modeNr > (ability.getModes().isEachModeMoreThanOnce() ? ability.getModes().getSelectedModes().size() : ability.getModes().size())) {
throw new UnsupportedOperationException("Given mode number (" + modeNr + ") not available for " + ability.toString());
}
UUID modeId = ability.getModes().getModeId(modeNr);
for (UUID currentModeId : ability.getModes().getSelectedModes()) {
Mode mode = ability.getModes().get(currentModeId);
if (mode.getId().equals(modeId)) {
selectedMode = mode;
ability.getModes().setActiveMode(mode);
index = 0; // reset target index if mode changes
break;
}
selectedMode = ability.getModes().get(modeId);
if (modeId != ability.getModes().getMode().getId()) {
ability.getModes().setActiveMode(modeId);
index = 0; // reset target index if mode changes
}
targetName = targetName.substring(6);
} else {

View file

@ -1131,7 +1131,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* additional by setcode e.g. "creatureName-M15" you can add [no copy] to
* the end of the target name to prohibite targets that are copied you can
* add [only copy] to the end of the target name to allow only targets that
* are copies
* are copies For modal spells use a prefix with the mode number:
* mode=1Lightning Bolt^mode=2Silvercoat Lion
*/
public void addTarget(TestPlayer player, String target) {
player.addTarget(target);

View file

@ -307,8 +307,7 @@ public abstract class AbilityImpl implements Ability {
return false;
}
for (UUID modeId : this.getModes().getSelectedModes()) {
Mode mode = this.getModes().get(modeId);
this.getModes().setActiveMode(mode);
this.getModes().setActiveMode(modeId);
//20121001 - 601.2c
// 601.2c The player announces his or her choice of an appropriate player, object, or zone for
// each target the spell requires. A spell may require some targets only if an alternative or
@ -330,7 +329,7 @@ public abstract class AbilityImpl implements Ability {
sourceObject.adjustTargets(this, game);
}
// Flashback abilities haven't made the choices the underlying spell might need for targetting.
if (!(this instanceof FlashbackAbility) && mode.getTargets().size() > 0 && mode.getTargets().chooseTargets(
if (!(this instanceof FlashbackAbility) && getTargets().size() > 0 && getTargets().chooseTargets(
getEffects().get(0).getOutcome(), this.controllerId, this, noMana, game) == false) {
if ((variableManaCost != null || announceString != null) && !game.isSimulation()) {
game.informPlayer(controller, (sourceObject != null ? sourceObject.getIdName() : "") + ": no valid targets with this value of X");

View file

@ -1,16 +1,16 @@
/*
* Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
@ -20,7 +20,7 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* 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.
@ -54,6 +54,10 @@ public class Mode implements Serializable {
this.effects = mode.effects.copy();
}
public UUID setRandomId() {
return this.id = UUID.randomUUID();
}
public Mode copy() {
return new Mode(this);
}

View file

@ -55,13 +55,14 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
private TargetController modeChooser;
private boolean eachModeMoreThanOnce; // each mode can be selected multiple times during one choice
private boolean eachModeOnlyOnce; // state if each mode can be chosen only once as long as the source object exists
private final LinkedHashMap<UUID, Mode> duplicateModes = new LinkedHashMap<>();
public Modes() {
this.currentMode = new Mode();
this.put(currentMode.getId(), currentMode);
this.minModes = 1;
this.maxModes = 1;
this.selectedModes.add(currentMode.getId());
this.addSelectedMode(currentMode.getId());
this.modeChooser = TargetController.YOU;
this.eachModeOnlyOnce = false;
this.eachModeMoreThanOnce = false;
@ -71,6 +72,9 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
for (Map.Entry<UUID, Mode> entry : modes.entrySet()) {
this.put(entry.getKey(), entry.getValue().copy());
}
for (Map.Entry<UUID, Mode> entry : modes.duplicateModes.entrySet()) {
this.put(entry.getKey(), entry.getValue().copy());
}
this.minModes = modes.minModes;
this.maxModes = modes.maxModes;
this.selectedModes.addAll(modes.getSelectedModes());
@ -78,7 +82,7 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
if (modes.getSelectedModes().isEmpty()) {
this.currentMode = values().iterator().next();
} else {
this.currentMode = get(selectedModes.get(0));
this.currentMode = get(modes.getMode().getId());
}
this.modeChooser = modes.modeChooser;
this.eachModeOnlyOnce = modes.eachModeOnlyOnce;
@ -89,16 +93,41 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
return new Modes(this);
}
@Override
public Mode get(Object key) {
Mode modeToGet = super.get(key);
if (modeToGet == null && eachModeMoreThanOnce) {
modeToGet = duplicateModes.get(key);
}
return modeToGet;
}
public Mode getMode() {
return currentMode;
}
/**
* Returns the mode by index. For modal spells with eachModeMoreThanOnce,
* the index returns the n selected mode
*
* @param index
* @return
*/
public UUID getModeId(int index) {
int idx = 0;
for (Mode mode : this.values()) {
idx++;
if (idx == index) {
return mode.getId();
if (eachModeMoreThanOnce) {
for (UUID modeId : this.selectedModes) {
idx++;
if (idx == index) {
return modeId;
}
}
} else {
for (Mode mode : this.values()) {
idx++;
if (idx == index) {
return mode.getId();
}
}
}
return null;
@ -138,6 +167,12 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
}
}
public void setActiveMode(UUID modeId) {
if (selectedModes.contains(modeId)) {
this.currentMode = get(modeId);
}
}
public void addMode(Mode mode) {
this.put(mode.getId(), mode);
}
@ -145,6 +180,7 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
public boolean choose(Game game, Ability source) {
if (this.size() > 1) {
this.selectedModes.clear();
this.duplicateModes.clear();
// check if mode modifying abilities exist
Card card = game.getCard(source.getSourceId());
if (card != null) {
@ -163,7 +199,7 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
for (Mode mode : this.values()) {
if ((!isEachModeOnlyOnce() || onceSelectedModes == null || !onceSelectedModes.contains(mode.getId()))
&& mode.getTargets().canChoose(source.getSourceId(), source.getControllerId(), game)) {
this.selectedModes.add(mode.getId());
this.addSelectedMode(mode.getId());
}
}
if (isEachModeOnlyOnce()) {
@ -200,7 +236,7 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
}
return this.selectedModes.size() >= this.getMinModes();
}
this.selectedModes.add(choice.getId());
this.addSelectedMode(choice.getId());
if (currentMode == null) {
currentMode = choice;
}
@ -209,17 +245,15 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
setAlreadySelectedModes(selectedModes, source, game);
}
return true;
} else { // only one mode
if (currentMode == null) {
this.selectedModes.clear();
Mode mode = this.values().iterator().next();
this.addSelectedMode(mode.getId());
this.setActiveMode(mode);
}
return true;
}
if (currentMode == null) {
this.selectedModes.clear();
Mode copiedMode = this.values().iterator().next().copy();
this.selectedModes.add(copiedMode.getId());
this.setActiveMode(copiedMode);
}
if (isEachModeOnlyOnce()) {
setAlreadySelectedModes(selectedModes, source, game);
}
return true;
}
/**
@ -236,6 +270,23 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
}
}
/**
* Adds a mode as selected. If the mode is already selected, it copies the
* mode and adds it to the duplicate modes
*
* @param modeId
*/
private void addSelectedMode(UUID modeId) {
if (selectedModes.contains(modeId) && eachModeMoreThanOnce) {
Mode duplicateMode = get(modeId).copy();
duplicateMode.setRandomId();
modeId = duplicateMode.getId();
duplicateModes.put(modeId, duplicateMode);
}
this.selectedModes.add(modeId);
}
// The already once selected modes for a modal card are stored as a state value
// That's important for modal abilities with modes that can only selected once while the object stays in its zone
@SuppressWarnings("unchecked")

View file

@ -34,6 +34,7 @@ import mage.constants.Duration;
import mage.constants.Outcome;
import mage.filter.FilterObject;
import mage.filter.FilterPermanent;
import mage.filter.FilterSpell;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
@ -93,6 +94,10 @@ public class CantBeTargetedAllEffect extends ContinuousRuleModifyingEffectImpl {
StackObject stackObject = game.getStack().getStackObject(event.getSourceId());
MageObject sourceObject;
if (stackObject instanceof StackAbility) {
if (filterSource instanceof FilterSpell) {
// only spells have to be selected
return false;
}
sourceObject = ((StackAbility) stackObject).getSourceObject(game);
} else {
sourceObject = stackObject;

View file

@ -146,7 +146,7 @@ public class DynamicManaEffect extends BasicManaEffect {
} else {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
ChoiceColor choiceColor = new ChoiceColor();
ChoiceColor choiceColor = new ChoiceColor(true);
for (int i = 0; i < count; i++) {
if (!choiceColor.isChosen()) {
while (!controller.choose(Outcome.Benefit, choiceColor, game)) {

View file

@ -33,6 +33,7 @@ import java.io.PrintWriter;
import java.util.*;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLayout;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
@ -166,16 +167,47 @@ public class Sets extends HashMap<String, ExpansionSet> {
}
}
// Write out all of the cards
for (Map.Entry<String, DeckCardInfo> entry: deckCards.entrySet()) {
out.printf("%d [%s:%s] %s%n", entry.getValue().getQuantity(), entry.getValue().getSetCode(), entry.getValue().getCardNum(), entry.getValue().getCardName());
}
for (Map.Entry<String, DeckCardInfo> entry: sideboard.entrySet()) {
out.printf("SB: %d [%s:%s] %s%n", entry.getValue().getQuantity(), entry.getValue().getSetCode(), entry.getValue().getCardNum(), entry.getValue().getCardName());
}
// Write out the layout
out.print("LAYOUT MAIN:");
writeCardLayout(out, deck.getCardLayout());
out.print("\n");
out.print("LAYOUT SIDEBOARD:");
writeCardLayout(out, deck.getSideboardLayout());
out.print("\n");
}
finally {
out.close();
}
}
private static void writeCardLayout(PrintWriter out, DeckCardLayout layout) {
List<List<List<DeckCardInfo>>> cardGrid = layout.getCards();
int height = cardGrid.size();
int width = (height > 0) ? cardGrid.get(0).size() : 0;
out.print("(" + height + "," + width + ")");
out.print(layout.getSettings());
out.print("|");
for (List<List<DeckCardInfo>> row : cardGrid) {
for (List<DeckCardInfo> stack : row) {
out.print("(");
for (int i = 0; i < stack.size(); ++i) {
DeckCardInfo info = stack.get(i);
out.printf("[%s:%s]", info.getSetCode(), info.getCardNum());
if (i != stack.size() - 1) {
out.print(",");
}
}
out.print(")");
}
}
}
}

View file

@ -48,9 +48,6 @@ public class Constructed extends DeckValidator {
protected List<String> setCodes = new ArrayList<>();
protected List<Rarity> rarities = new ArrayList<>();
protected boolean allowAllCustomSets = false;
protected Set<String> allowedCustomSetCodes = new HashSet<>();
public Constructed() {
super("Constructed");
}
@ -173,12 +170,7 @@ public class Constructed extends DeckValidator {
* @return Whether the set is legal in this format.
*/
protected boolean isSetAllowed(String code) {
// To check here for custom set makes no sens IMHO because the format does define what's aloowed and what not
// if (Sets.isCustomSet(code)) {
// return allowAllCustomSets || allowedCustomSetCodes.contains(code);
// } else {
return setCodes.isEmpty() || setCodes.contains(code);
// }
}
/**

View file

@ -44,6 +44,8 @@ public class Deck implements Serializable {
private String name;
private final Set<Card> cards = new LinkedHashSet<>();
private final Set<Card> sideboard = new LinkedHashSet<>();
private DeckCardLayout cardsLayout;
private DeckCardLayout sideboardLayout;
private long deckHashCode = 0;
public static Deck load(DeckCardLists deckCardLists) throws GameException {
@ -57,6 +59,8 @@ public class Deck implements Serializable {
public static Deck load(DeckCardLists deckCardLists, boolean ignoreErrors, boolean mockCards) throws GameException {
Deck deck = new Deck();
deck.setName(deckCardLists.getName());
deck.cardsLayout = deckCardLists.getCardLayout();
deck.sideboardLayout = deckCardLists.getSideboardLayout();
List<String> deckCardNames = new ArrayList<>();
for (DeckCardInfo deckCardInfo : deckCardLists.getCards()) {
Card card = createCard(deckCardInfo, mockCards);
@ -141,10 +145,18 @@ public class Deck implements Serializable {
return cards;
}
public DeckCardLayout getCardsLayout() {
return cardsLayout;
}
public Set<Card> getSideboard() {
return sideboard;
}
public DeckCardLayout getSideboardLayout() {
return sideboardLayout;
}
public long getDeckHashCode() {
return deckHashCode;
}

View file

@ -0,0 +1,24 @@
package mage.cards.decks;
import java.util.List;
/**
* Created by stravant@gmail.com on 2016-10-03.
*/
public class DeckCardLayout {
private List<List<List<DeckCardInfo>>> cards;
private String settings;
public DeckCardLayout(List<List<List<DeckCardInfo>>> cards, String settings) {
this.cards = cards;
this.settings = settings;
}
public List<List<List<DeckCardInfo>>> getCards() {
return cards;
}
public String getSettings() {
return settings;
}
}

View file

@ -42,6 +42,26 @@ public class DeckCardLists implements Serializable {
private List<DeckCardInfo> cards = new ArrayList<>();
private List<DeckCardInfo> sideboard = new ArrayList<>();
// Layout (if supported)
private DeckCardLayout cardLayout = null;
private DeckCardLayout sideboardLayout = null;
/**
* @return The layout of the cards
*/
public DeckCardLayout getCardLayout() {
return cardLayout;
}
public void setCardLayout(DeckCardLayout layout) {
this.cardLayout = layout;
}
public DeckCardLayout getSideboardLayout() {
return sideboardLayout;
}
public void setSideboardLayout(DeckCardLayout layout) {
this.sideboardLayout = layout;
}
/**
* @return the cards
*/

View file

@ -27,9 +27,12 @@
*/
package mage.cards.decks.importer;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLayout;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
@ -42,6 +45,12 @@ public class DckDeckImporter extends DeckImporter {
private static final Pattern pattern = Pattern.compile("(SB:)?\\s*(\\d*)\\s*\\[([^]:]+):([^]:]+)\\].*");
private static final Pattern layoutPattern = Pattern.compile("LAYOUT (\\w+):\\((\\d+),(\\d+)\\)([^|]+)\\|(.*)$");
private static final Pattern layoutStackPattern = Pattern.compile("\\(([^)]*)\\)");
private static final Pattern layoutStackEntryPattern = Pattern.compile("\\[(\\w+):(\\w+)]");
@Override
protected void readLine(String line, DeckCardLists deckList) {
@ -79,6 +88,56 @@ public class DckDeckImporter extends DeckImporter {
deckList.setName(line.substring(5, line.length()));
} else if (line.startsWith("AUTHOR:")) {
deckList.setAuthor(line.substring(7, line.length()));
} else if (line.startsWith("LAYOUT")) {
Matcher m2 = layoutPattern.matcher(line);
if (m2.find()) {
String target = m2.group(1);
int rows = Integer.parseInt(m2.group(2));
int cols = Integer.parseInt(m2.group(3));
String settings = m2.group(4);
String stackData = m2.group(5);
Matcher stackMatcher = layoutStackPattern.matcher(stackData);
//
List<List<List<DeckCardInfo>>> grid = new ArrayList<>();
int totalCardCount = 0;
for (int row = 0; row < rows; ++row) {
List<List<DeckCardInfo>> rowData = new ArrayList<>();
grid.add(rowData);
for (int col = 0; col < cols; ++col) {
List<DeckCardInfo> stack = new ArrayList<>();
rowData.add(stack);
if (stackMatcher.find()) {
String thisStackData = stackMatcher.group(1);
Matcher stackEntries = layoutStackEntryPattern.matcher(thisStackData);
while (stackEntries.find()) {
++totalCardCount;
stack.add(new DeckCardInfo("", stackEntries.group(2), stackEntries.group(1)));
}
} else {
sbMessage.append("Missing stack\n.");
}
}
}
//
DeckCardLayout layout = new DeckCardLayout(grid, settings);
int expectedCount = 0;
if (target.equals("MAIN")) {
deckList.setCardLayout(layout);
expectedCount = deckList.getCards().size();
} else if (target.equals("SIDEBOARD")) {
deckList.setSideboardLayout(layout);
expectedCount = deckList.getSideboard().size();
} else {
sbMessage.append("Bad target `" + target + "` for layout.\n");
}
//
if (totalCardCount != expectedCount) {
sbMessage.append("Layout mismatch: Expected " + expectedCount + " cards, but got " + totalCardCount + " in layout `" + target + "`\n.");
}
} else {
sbMessage.append("Malformed layout line");
}
}
}
}

View file

@ -211,9 +211,8 @@ public class Spell extends StackObjImpl implements Card {
for (SpellAbility spellAbility : this.spellAbilities) {
if (spellAbilityHasLegalParts(spellAbility, game)) {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
Mode mode = spellAbility.getModes().get(modeId);
spellAbility.getModes().setActiveMode(mode);
if (mode.getTargets().stillLegal(spellAbility, game)) {
spellAbility.getModes().setActiveMode(modeId);
if (spellAbility.getTargets().stillLegal(spellAbility, game)) {
if (!spellAbility.getSpellAbilityType().equals(SpellAbilityType.SPLICE)) {
updateOptionalCosts(index);
}