mirror of
https://github.com/correl/mage.git
synced 2025-01-12 19:25:44 +00:00
Merge origin/master
This commit is contained in:
commit
3c7c584bab
34 changed files with 823 additions and 351 deletions
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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<>();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
24
Mage/src/main/java/mage/cards/decks/DeckCardLayout.java
Normal file
24
Mage/src/main/java/mage/cards/decks/DeckCardLayout.java
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue