1
0
Fork 0
mirror of https://github.com/correl/mage.git synced 2025-04-02 03:18:09 -09:00

Merge branch 'master' into GlitterLionTextFix

This commit is contained in:
18ths 2020-07-01 10:03:37 +02:00 committed by GitHub
commit 653ca9cc5a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2083 changed files with 40145 additions and 16096 deletions
Mage.Client
Mage.Common
Mage.Plugins
Mage.Counter.Plugin
pom.xml
Mage.Server.Console
pom.xml
src/main/java/mage/server/console
Mage.Server.Plugins
Mage.Deck.Constructed
Mage.Deck.Limited
Mage.Game.BrawlDuel
Mage.Game.BrawlFreeForAll
Mage.Game.CanadianHighlanderDuel
Mage.Game.CommanderDuel
Mage.Game.CommanderFreeForAll
Mage.Game.FreeForAll
Mage.Game.FreeformCommanderDuel
Mage.Game.FreeformCommanderFreeForAll
Mage.Game.FreeformUnlimitedCommander
Mage.Game.MomirDuel
Mage.Game.MomirGame
Mage.Game.OathbreakerDuel
Mage.Game.OathbreakerFreeForAll
Mage.Game.PennyDreadfulCommanderFreeForAll
Mage.Game.TinyLeadersDuel
Mage.Game.TwoPlayerDuel
Mage.Player.AI.DraftBot
Mage.Player.AI.MA
Mage.Player.AI
pom.xml
src/main/java/mage/player/ai
Mage.Player.AIMCTS
Mage.Player.AIMinimax
pom.xml
src/mage/player/ai
Mage.Player.Human
pom.xml
src/mage/player/human
Mage.Tournament.BoosterDraft
pom.xml
src/mage/tournament/cubes
Mage.Tournament.Constructed
Mage.Tournament.Sealed
pom.xml
Mage.Server
Mage.Sets

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-root</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-client</artifactId>

View file

@ -202,7 +202,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
config.setAccessPreference(FsAccessOption.STORE, true);
try {
UIManager.put("desktop", new Color(0, 0, 0, 0));
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
//UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
// stop JSplitPane from eating F6 and F8 or any other function keys
{

View file

@ -562,6 +562,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
JButton selectByButton;
JButton analyseButton;
JButton blingButton;
JButton oldVersionButton;
// Popup for toolbar
final JPopupMenu filterPopup;
@ -716,6 +717,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
selectByButton = new JButton("Select By");
analyseButton = new JButton("M"); // "Mana" button
blingButton = new JButton("B"); // "Bling" button
oldVersionButton = new JButton("O"); // "Old version" button
// Name and count label
deckNameAndCountLabel = new JLabel();
@ -740,6 +742,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
toolbarInner.add(visibilityButton);
toolbarInner.add(analyseButton);
toolbarInner.add(blingButton);
toolbarInner.add(oldVersionButton);
toolbar.add(toolbarInner, BorderLayout.WEST);
JPanel sliderPanel = new JPanel(new GridBagLayout());
sliderPanel.setOpaque(false);
@ -982,6 +985,11 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
blingButton.addActionListener(evt -> blingDeck());
// Old version button - Switch cards to the oldest non-promo printing. In case of multiples in a set, take the lowest card number.
oldVersionButton.setToolTipText("Switch cards to the oldest non-promo printing");
oldVersionButton.addActionListener(evt -> oldVersionDeck());
// Filter popup
filterPopup = new JPopupMenu();
filterPopup.setPreferredSize(new Dimension(300, 300));
@ -1567,6 +1575,44 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
}
}
private void oldVersionDeck() {
if (this.mode != Constants.DeckEditorMode.FREE_BUILDING) {
return;
}
if (JOptionPane.showConfirmDialog(null, "Are you sure you want to switch your card versions to the oldest ones?", "WARNING",
JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
return;
}
List<List<List<CardView>>> newCardGrid = new ArrayList<>();
for (List<List<CardView>> gridRow : cardGrid) {
List<List<CardView>> newGridRow = new ArrayList<>();
for (List<CardView> stack : gridRow) {
List<CardView> newStack = new ArrayList<>();
for (CardView card : stack) {
CardInfo oldestCardInfo = CardRepository.instance.findOldestNonPromoVersionCard(card.getName());
if (oldestCardInfo != null) {
CardView oldestCardView = new CardView(oldestCardInfo.getMockCard());
this.removeCardView(card);
eventSource.fireEvent(card, ClientEventType.REMOVE_SPECIFIC_CARD);
this.addCardView(oldestCardView, false);
eventSource.fireEvent(oldestCardView, ClientEventType.ADD_SPECIFIC_CARD);
newStack.add(oldestCardView);
} else {
newStack.add(card);
}
}
newGridRow.add(newStack);
}
newCardGrid.add(newGridRow);
}
cardGrid = newCardGrid;
layoutGrid();
cardScroll.revalidate();
repaint();
}
// Update the contents of the card grid
public void setCards(CardsView cardsView, DeckCardLayout layout, BigCard bigCard) {
if (bigCard != null) {

View file

@ -1,10 +1,5 @@
package mage.client.components;
//import com.sun.java.swing.Painter;
import java.awt.Color;
import java.awt.Graphics2D;
import javax.swing.JDesktopPane;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
@ -22,7 +17,7 @@ public class MageJDesktop extends JDesktopPane {
public void updateUI() {
if ("Nimbus".equals(UIManager.getLookAndFeel().getName())) {
UIDefaults map = new UIDefaults();
Painter painter = (g, c, w, h) -> {
Painter<?> painter = (g, c, w, h) -> {
g.setColor( UIManager.getDefaults().getColor("desktop") );
g.fillRect(0,0,w,h);
};

View file

@ -383,7 +383,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
public static void main(String[] argv) {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception ex) {
}

View file

@ -562,8 +562,8 @@ public class MageBook extends JComponent {
Constructor<?> cons = c.getConstructor();
Object newToken = cons.newInstance();
if (newToken instanceof Token) {
((Token) newToken).setExpansionSetCodeForImage(set);
((Token) newToken).setOriginalExpansionSetCode(set);
((Token) newToken).setExpansionSetCodeForImage(set);
((Token) newToken).setTokenType(token.getType());
tokens.add((Token) newToken);
}
@ -634,6 +634,10 @@ public class MageBook extends JComponent {
}
}
if (emblems.size() == 0) {
return emblems;
}
int totalTokens = getTotalNumTokens(set);
int start = 0;
if (!(page * conf.CARDS_PER_PAGE <= totalTokens && (page + 1) * conf.CARDS_PER_PAGE >= totalTokens)) {
@ -917,8 +921,8 @@ public class MageBook extends JComponent {
private int currentPage = 0;
private String currentSet = "RTR";
private int currentCardsInSet = 0;
private int currentCardsNotInSet = 0;
private final int currentCardsInSet = 0;
private final int currentCardsNotInSet = 0;
private static CardDimensions cardDimensions = new CardDimensions(1.2d);
private static final Logger log = Logger.getLogger(MageBook.class);

View file

@ -1,5 +1,14 @@
package mage.client.dialog;
import java.awt.*;
import java.beans.PropertyVetoException;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
import javax.swing.*;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import mage.client.cards.BigCard;
import mage.client.util.GUISizeHelper;
import mage.client.util.ImageHelper;
@ -13,15 +22,6 @@ import mage.view.SimpleCardsView;
import org.apache.log4j.Logger;
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
import javax.swing.*;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import java.awt.*;
import java.beans.PropertyVetoException;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
/**
* @author BetaSteward_at_googlemail.com, JayDi85
*/
@ -77,7 +77,7 @@ public class CardInfoWindowDialog extends MageDialog {
this.setClosable(false);
break;
default:
// no icon yet
// no icon yet
}
this.setTitelBarToolTip(name);
setGUISize();
@ -174,13 +174,17 @@ public class CardInfoWindowDialog extends MageDialog {
Set<String> cardTypesPresent = new LinkedHashSet<String>() {
};
for (CardView card : cardsView.values()) {
Set<CardType> cardTypes = card.getCardTypes();
Set<CardType> cardTypes = EnumSet.noneOf(CardType.class);
cardTypes.addAll(card.getCardTypes());
for (CardType cardType : cardTypes) {
cardTypesPresent.add(cardType.toString());
}
}
if (cardTypesPresent.isEmpty()) return 0;
else return cardTypesPresent.size();
if (cardTypesPresent.isEmpty()) {
return 0;
} else {
return cardTypesPresent.size();
}
}
/**

View file

@ -890,7 +890,7 @@ public class NewTableDialog extends MageDialog {
this.spnFreeMulligans.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS + versionStr, "0")));
this.cbMulligan.setSelectedItem(MulliganType.valueByName(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_MULLIGAN_TYPE + versionStr, MulliganType.GAME_DEFAULT.toString())));
int range = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_RANGE + versionStr, "1"));
int range = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_RANGE + versionStr, "0"));
for (RangeOfInfluence roi : RangeOfInfluence.values()) {
if (roi.getRange() == range) {
this.cbRange.setSelectedItem(roi);

View file

@ -1203,6 +1203,7 @@ public class NewTournamentDialog extends MageDialog {
tOptions.getLimitedOptions().setConstructionTime((Integer) this.spnConstructTime.getValue() * 60);
tOptions.getLimitedOptions().setIsRandom(tournamentType.isRandom());
tOptions.getLimitedOptions().setIsRichMan(tournamentType.isRichMan());
tOptions.getLimitedOptions().setIsJumpstart(tournamentType.isJumpstart());
if (tournamentType.isCubeBooster()) {
tOptions.getLimitedOptions().setDraftCubeName(this.cbDraftCube.getSelectedItem().toString());
if (!(cubeFromDeckFilename.isEmpty())) {

View file

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.9" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JInternalFrameFormInfo">
<Properties>
<Property name="resizable" type="boolean" value="true"/>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
</SyntheticProperties>
@ -39,7 +42,7 @@
<EmptySpace max="-2" attributes="0"/>
<Component id="panelSearch" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="scrollList" pref="246" max="32767" attributes="0"/>
<Component id="scrollList" pref="282" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="panelCommands" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
@ -55,8 +58,8 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="labelMessage" alignment="1" pref="210" max="32767" attributes="0"/>
<Component id="labelSubMessage" alignment="1" pref="210" max="32767" attributes="0"/>
<Component id="labelMessage" alignment="1" pref="337" max="32767" attributes="0"/>
<Component id="labelSubMessage" alignment="1" pref="337" max="32767" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
@ -203,4 +206,4 @@
</SubComponents>
</Container>
</SubComponents>
</Form>
</Form>

View file

@ -1,25 +1,22 @@
package mage.client.dialog;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import mage.choices.Choice;
import mage.client.MageFrame;
import mage.client.util.gui.MageDialogState;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
/**
* @author JayDi85
*/
public class PickChoiceDialog extends MageDialog {
Choice choice;
List<KeyValueItem> allItems = new ArrayList<>();
java.util.List<KeyValueItem> allItems = new ArrayList<>();
DefaultListModel<KeyValueItem> dataModel = new DefaultListModel<>();
final private static String HTML_TEMPLATE = "<html><div style='text-align: center;'>%s</div></html>";
@ -79,7 +76,7 @@ public class PickChoiceDialog extends MageDialog {
this.editSearch.setText("");
}
// listeners for inremental filtering
// listeners for inremental filtering
editSearch.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
@ -104,7 +101,7 @@ public class PickChoiceDialog extends MageDialog {
editSearch.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
//System.out.println("types");
//System.out.println("types");
}
@Override
@ -152,8 +149,11 @@ public class PickChoiceDialog extends MageDialog {
} else {
MageFrame.getDesktop().add(this, JLayeredPane.PALETTE_LAYER);
}
if (mageDialogState != null) mageDialogState.setStateToDialog(this);
else this.makeWindowCentered();
if (mageDialogState != null) {
mageDialogState.setStateToDialog(this);
} else {
this.makeWindowCentered();
}
// final load
loadData();
@ -269,6 +269,7 @@ public class PickChoiceDialog extends MageDialog {
}
class KeyValueItem {
private final String Key;
private final String Value;
@ -312,6 +313,8 @@ public class PickChoiceDialog extends MageDialog {
btOK = new javax.swing.JButton();
btCancel = new javax.swing.JButton();
setResizable(true);
labelMessage.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
labelMessage.setText("<html><div style='text-align: center;'>example long message example long message example long message example long message example long message</div></html>");
@ -322,20 +325,20 @@ public class PickChoiceDialog extends MageDialog {
javax.swing.GroupLayout panelHeaderLayout = new javax.swing.GroupLayout(panelHeader);
panelHeader.setLayout(panelHeaderLayout);
panelHeaderLayout.setHorizontalGroup(
panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelHeaderLayout.createSequentialGroup()
.addGroup(panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(labelMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 210, Short.MAX_VALUE)
.addComponent(labelSubMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 210, Short.MAX_VALUE))
.addGap(0, 0, 0))
panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelHeaderLayout.createSequentialGroup()
.addGroup(panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(labelMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 337, Short.MAX_VALUE)
.addComponent(labelSubMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 337, Short.MAX_VALUE))
.addGap(0, 0, 0))
);
panelHeaderLayout.setVerticalGroup(
panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelHeaderLayout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(labelMessage)
.addGap(0, 0, 0)
.addComponent(labelSubMessage))
panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelHeaderLayout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(labelMessage)
.addGap(0, 0, 0)
.addComponent(labelSubMessage))
);
labelSearch.setText("Search:");
@ -345,34 +348,28 @@ public class PickChoiceDialog extends MageDialog {
javax.swing.GroupLayout panelSearchLayout = new javax.swing.GroupLayout(panelSearch);
panelSearch.setLayout(panelSearchLayout);
panelSearchLayout.setHorizontalGroup(
panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelSearchLayout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(labelSearch)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editSearch)
.addGap(0, 0, 0))
panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelSearchLayout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(labelSearch)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editSearch)
.addGap(0, 0, 0))
);
panelSearchLayout.setVerticalGroup(
panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelSearchLayout.createSequentialGroup()
.addGap(3, 3, 3)
.addGroup(panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(labelSearch)
.addComponent(editSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(3, 3, 3))
panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelSearchLayout.createSequentialGroup()
.addGap(3, 3, 3)
.addGroup(panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(labelSearch)
.addComponent(editSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(3, 3, 3))
);
listChoices.setModel(new javax.swing.AbstractListModel() {
String[] strings = {"item1", "item2", "item3"};
public int getSize() {
return strings.length;
}
public Object getElementAt(int i) {
return strings[i];
}
String[] strings = { "item1", "item2", "item3" };
public int getSize() { return strings.length; }
public Object getElementAt(int i) { return strings[i]; }
});
scrollList.setViewportView(listChoices);
@ -393,25 +390,25 @@ public class PickChoiceDialog extends MageDialog {
javax.swing.GroupLayout panelCommandsLayout = new javax.swing.GroupLayout(panelCommands);
panelCommands.setLayout(panelCommandsLayout);
panelCommandsLayout.setHorizontalGroup(
panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelCommandsLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btOK)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelCommandsLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btOK)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
panelCommandsLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, btCancel, btOK);
panelCommandsLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {btCancel, btOK});
panelCommandsLayout.setVerticalGroup(
panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelCommandsLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btCancel)
.addComponent(btOK))
.addContainerGap())
panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelCommandsLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btCancel)
.addComponent(btOK))
.addContainerGap())
);
getRootPane().setDefaultButton(btOK);
@ -419,28 +416,28 @@ public class PickChoiceDialog extends MageDialog {
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollList, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(panelCommands, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(panelHeader, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(panelSearch, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollList, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(panelCommands, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(panelHeader, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(panelSearch, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(panelHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(panelSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(scrollList, javax.swing.GroupLayout.DEFAULT_SIZE, 246, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(panelCommands, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(panelHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(panelSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(scrollList, javax.swing.GroupLayout.DEFAULT_SIZE, 282, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(panelCommands, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
pack();
@ -474,4 +471,4 @@ public class PickChoiceDialog extends MageDialog {
private javax.swing.JPanel panelSearch;
private javax.swing.JScrollPane scrollList;
// End of variables declaration//GEN-END:variables
}
}

View file

@ -1,5 +1,13 @@
package mage.client.game;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.*;
import javax.swing.*;
import javax.swing.GroupLayout.Alignment;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import mage.cards.decks.importer.DckDeckImporter;
import mage.client.MageFrame;
import mage.client.SessionHandler;
@ -15,6 +23,7 @@ import mage.client.util.gui.countryBox.CountryUtil;
import mage.components.ImagePanel;
import mage.components.ImagePanelStyle;
import mage.constants.CardType;
import static mage.constants.Constants.*;
import mage.constants.ManaType;
import mage.counters.Counter;
import mage.counters.CounterType;
@ -23,17 +32,6 @@ import mage.utils.timer.PriorityTimer;
import mage.view.*;
import org.mage.card.arcane.ManaSymbols;
import javax.swing.*;
import javax.swing.GroupLayout.Alignment;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.*;
import static mage.constants.Constants.*;
/**
* Enhanced player pane.
*
@ -42,7 +40,6 @@ import static mage.constants.Constants.*;
public class PlayerPanelExt extends javax.swing.JPanel {
// TODO: *.form file was lost, panel must be reworks in designer
private UUID playerId;
private UUID gameId;
private PlayerView player;
@ -612,7 +609,6 @@ public class PlayerPanelExt extends javax.swing.JPanel {
manaCountLabelW.addMouseListener(manaMouseAdapter);
manaLabels.put(manaCountLabelW, ManaType.WHITE);l
//*/
///*
JLabel manaCountLabelW = new JLabel();
manaCountLabelW.setToolTipText("White mana");
@ -944,7 +940,8 @@ public class PlayerPanelExt extends javax.swing.JPanel {
Set<String> cardTypesPresent = new LinkedHashSet<String>() {
};
for (CardView card : cardsView.values()) {
Set<CardType> cardTypes = card.getCardTypes();
Set<CardType> cardTypes = EnumSet.noneOf(CardType.class);
cardTypes.addAll(card.getCardTypes());
for (CardType cardType : cardTypes) {
cardTypesPresent.add(cardType.toString());
}

View file

@ -45,7 +45,7 @@ public class NewPlayerPanel extends javax.swing.JPanel {
this.txtPlayerName.setEditable(false);
this.txtPlayerName.setEnabled(false);
}
protected void playerLoadDeck() {
String lastFolder = MageFrame.getPreferences().get("lastDeckFolder", "");
if (!lastFolder.isEmpty()) {

View file

@ -126,7 +126,9 @@ public class TablePlayerPanel extends javax.swing.JPanel {
private void cbPlayerTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbPlayerTypeActionPerformed
if (getPlayerType() != PlayerType.HUMAN) {
this.newPlayerPanel.setVisible(true);
} else {
this.newPlayerPanel.setPlayerName("Computer " + this.lblPlayerNum.getText().charAt(this.lblPlayerNum.getText().length() - 1));
}
else {
this.newPlayerPanel.setVisible(false);
}
this.revalidate();

View file

@ -15,16 +15,10 @@ public class CardViewRarityComparator implements Comparator<CardView> {
Rarity r1 = o1.getRarity();
Rarity r2 = o2.getRarity();
int val = Integer.compare(
return Integer.compare(
r1 == null ? 0 : r1.getSorting(),
r2 == null ? 0 : r2.getSorting()
);
if (val == 0) {
return o1.getName().compareTo(o2.getName());
} else {
return val;
}
}
}

View file

@ -14,9 +14,8 @@ import org.mage.card.arcane.UI;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.*;
import static mage.client.dialog.PreferencesDialog.KEY_MAGE_PANEL_LAST_SIZE;
@ -344,6 +343,9 @@ public final class GuiDisplayUtil {
}
buffer.append("</td></tr></table>");
// split card rules shows up by parts, so no needs to duplicate it later (only dynamic abilities must be shown)
Set<String> duplicatedRules = new HashSet<>();
StringBuilder rule = new StringBuilder("<br/>");
if (card.isSplitCard()) {
rule.append("<table cellspacing=0 cellpadding=0 border=0 width='100%'>");
@ -356,7 +358,9 @@ public final class GuiDisplayUtil {
rule.append("</td></tr></table>");
for (String ruling : card.getLeftSplitRules()) {
if (ruling != null && !ruling.replace(".", "").trim().isEmpty()) {
rule.append("<p style='margin: 2px'>").append(ruling).append("</p>");
// split names must be replaced
duplicatedRules.add(ruling);
rule.append("<p style='margin: 2px'>").append(replaceNamesInRule(ruling, card.getLeftSplitName())).append("</p>");
}
}
rule.append("<table cellspacing=0 cellpadding=0 border=0 width='100%'>");
@ -369,13 +373,18 @@ public final class GuiDisplayUtil {
rule.append("</td></tr></table>");
for (String ruling : card.getRightSplitRules()) {
if (ruling != null && !ruling.replace(".", "").trim().isEmpty()) {
rule.append("<p style='margin: 2px'>").append(ruling).append("</p>");
// split names must be replaced
duplicatedRules.add(ruling);
rule.append("<p style='margin: 2px'>").append(replaceNamesInRule(ruling, card.getRightSplitName())).append("</p>");
}
}
}
if (!textLines.getLines().isEmpty()) {
for (String textLine : textLines.getLines()) {
if (textLine != null && !textLine.replace(".", "").trim().isEmpty()) {
if (duplicatedRules.contains(textLine)) {
continue;
}
rule.append("<p style='margin: 2px'>").append(textLine).append("</p>");
}
}
@ -383,8 +392,7 @@ public final class GuiDisplayUtil {
String legal = rule.toString();
if (!legal.isEmpty()) {
legal = legal.replaceAll("\\{this\\}", card.getName().isEmpty() ? "this" : card.getName());
legal = legal.replaceAll("\\{source\\}", card.getName().isEmpty() ? "this" : card.getName());
legal = replaceNamesInRule(legal, card.getDisplayName()); // must show real display name (e.g. split part, not original card)
buffer.append(ManaSymbols.replaceSymbolsWithHTML(legal, ManaSymbols.Type.TOOLTIP));
}
@ -397,6 +405,12 @@ public final class GuiDisplayUtil {
return buffer;
}
private static String replaceNamesInRule(String rule, String cardName) {
String res = rule.replaceAll("\\{this\\}", cardName.isEmpty() ? "this" : cardName);
res = res.replaceAll("\\{source\\}", cardName.isEmpty() ? "this" : cardName);
return res;
}
private static String getResourcePath(String image) {
return GuiDisplayUtil.class.getClassLoader().getResource(image).toString();
}

View file

@ -154,6 +154,7 @@ public class CardPanelRenderImpl extends CardPanel {
sb.append(this.view.getToughness());
sb.append(this.view.getLoyalty());
sb.append(this.view.getColor().toString());
sb.append(this.view.getType());
sb.append(this.view.getExpansionSetCode());
for (CardType type : this.view.getCardTypes()) {
sb.append((char) type.ordinal());
@ -227,7 +228,7 @@ public class CardPanelRenderImpl extends CardPanel {
private BufferedImage faceArtImage;
// Factory to generate card appropriate views
private CardRendererFactory cardRendererFactory = new CardRendererFactory();
private final CardRendererFactory cardRendererFactory = new CardRendererFactory();
// The rendered card image, with or without the art image loaded yet
// = null while invalid

View file

@ -140,13 +140,30 @@ public abstract class CardRenderer {
break;
}
// workaround to use real split card names
String realCardName = cardView.getDisplayName();
if (cardView.isSplitCard()) {
for (String partRule : cardView.getLeftSplitRules()) {
if (partRule.equals(rule)) {
realCardName = cardView.getLeftSplitName();
break;
}
}
for (String partRule : cardView.getRightSplitRules()) {
if (partRule.equals(rule)) {
realCardName = cardView.getRightSplitName();
break;
}
}
}
// Kill reminder text
if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_REMINDER_TEXT, "false").equals("false")) {
rule = CardRendererUtils.killReminderText(rule).trim();
}
if (!rule.isEmpty()) {
TextboxRule tbRule = TextboxRuleParser.parse(cardView, rule);
TextboxRule tbRule = TextboxRuleParser.parse(cardView, rule, realCardName);
if (tbRule.type == TextboxRuleType.SIMPLE_KEYWORD) {
keywords.add(tbRule);
} else if (tbRule.text.isEmpty()) {

View file

@ -7,6 +7,7 @@ package org.mage.card.arcane;
import java.awt.Font;
import java.awt.Image;
import java.awt.Paint;
import java.awt.font.GraphicAttribute;
import java.awt.font.ImageGraphicAttribute;
import java.awt.font.TextAttribute;
@ -47,6 +48,25 @@ public class TextboxRule {
}
}
public static class ColorRegion implements AttributeRegion {
ColorRegion(int start, int end, Paint color) {
this.start = start;
this.end = end;
this.color = color;
}
private final int start;
private final int end;
private final Paint color;
@Override
public void applyToAttributedString(AttributedString str, Font normal, Font italic) {
if (end > start + 1) {
str.addAttribute(TextAttribute.FOREGROUND, color, start, end);
}
}
}
// A special symbol embedded at some point in a string
public static class EmbeddedSymbol implements AttributeRegion {

View file

@ -23,13 +23,14 @@ public final class TextboxRuleParser {
private static final Pattern LevelAbilityPattern = Pattern.compile("Level (\\d+)-?(\\d*)(\\+?)");
private static final Pattern LoyaltyAbilityPattern = Pattern.compile("^(\\+|\\-)(\\d+|X): ");
private static final Pattern SimpleKeywordPattern = Pattern.compile("^(\\w+( \\w+)?)\\s*(\\([^\\)]*\\))?\\s*$");
private static final Pattern FontColorValuePattern = Pattern.compile("color\\s*=\\s*[\"'](\\w+)[\"']");
// Parse a given rule (given as a string) into a TextboxRule, replacing
// symbol annotations, italics, etc, parsing out information such as
// if the ability is a loyalty ability, and returning an TextboxRule
// representing that information, which can be used to render the rule in
// the textbox of a card.
public static TextboxRule parse(CardView source, String rule) {
public static TextboxRule parse(CardView source, String rule, String cardNameToUse) {
// List of regions to apply
List<TextboxRule.AttributeRegion> regions = new ArrayList<>();
@ -103,10 +104,9 @@ public final class TextboxRuleParser {
String contents = rule.substring(index + 1, closeIndex);
if (contents.equals("this") || contents.equals("source")) {
// Replace {this} with the card's name
String cardName = source.getName();
build.append(cardName);
build.append(cardNameToUse);
index += contents.length() + 2;
outputIndex += cardName.length();
outputIndex += cardNameToUse.length();
} else {
Image symbol = ManaSymbols.getSizedManaSymbol(contents.replace("/", ""), 10);
if (symbol != null) {
@ -200,6 +200,11 @@ public final class TextboxRuleParser {
}
}
break;
case "/font":
// Font it is an additional info of a card
// lets make it blue like it is in tooltip
regions.add(new TextboxRule.ColorRegion(openingIndex, outputIndex, Color.BLUE));
break;
default:
// Unknown
build.append('<').append(tag).append('>');

View file

@ -18,14 +18,13 @@ public enum GrabbagImageSource implements CardImageSource {
private static final Set<String> supportedSets = new LinkedHashSet<String>() {
{
add("PTC");
add("SWS");
}
};
@Override
public String getSourceName() {
return "";
return "Grabbag";
}
@Override
@ -76,16 +75,6 @@ public enum GrabbagImageSource implements CardImageSource {
return;
}
singleLinks = new HashMap<>();
singleLinks.put("PTC/Arbiter of the Ideal", "MTG/BNG/en/promo/ArbiterOfTheIdeal.jpg");
singleLinks.put("PTC/Courser of Kruphix", "MTG/BNG/en/promo/CourserOfKruphix.jpg");
singleLinks.put("PTC/Eater of Hope", "MTG/BNG/en/promo/EaterOfHope.jpg");
singleLinks.put("PTC/Fated Return", "MTG/BNG/en/promo/FatedReturn.jpg");
singleLinks.put("PTC/Forgestoker Dragon", "MTG/BNG/en/promo/ForgestokerDragon.jpg");
singleLinks.put("PTC/Nessian Wilds Ravager", "MTG/BNG/en/promo/NessianWildsRavager.jpg");
singleLinks.put("PTC/Pain Seer", "MTG/BNG/en/promo/PainSeer.jpg");
singleLinks.put("PTC/Silent Sentinel", "MTG/BNG/en/promo/SilentSentinel.jpg");
singleLinks.put("PTC/Tromokratis", "MTG/BNG/en/promo/Tromokratis.jpg");
singleLinks.put("SWS/AAT-1", "CqmDY8V.jpg");
singleLinks.put("SWS/Acklay of the Arena", "ESVRm6F.jpg");
singleLinks.put("SWS/Acquire Target", "FOskB4q.jpg");
@ -478,7 +467,7 @@ public enum GrabbagImageSource implements CardImageSource {
if (card.getSet().equals("MTG")) {
return "http://static.starcitygames.com/sales/cardscans/";
} else if (card.getSet().equals("SWS")) {
return "http://i.imgur.com/";
return "https://i.imgur.com/";
} else {
return "http://magiccards.info/scans/en/";
}

View file

@ -260,6 +260,8 @@ public class ScryfallImageSupportCards {
add("PPP1");
add("PF19");
add("MPS-AKH");
add("M21");
add("JMP");
}
};

View file

@ -94,16 +94,33 @@ public class ScryfallImageSupportTokens {
put("HOU/Insect", "https://api.scryfall.com/cards/thou/12/en?format=image");
put("HOU/Snake", "https://api.scryfall.com/cards/thou/11/en?format=image");
//AKH
//AKH - tokens
put("AKH/Beast", "https://api.scryfall.com/cards/takh/21/en?format=image");
put("AKH/Cat", "https://api.scryfall.com/cards/takh/16/en?format=image");
put("AKH/Drake", "https://api.scryfall.com/cards/takh/18/en?format=image");
put("AKH/Emblem Gideon", "https://api.scryfall.com/cards/takh/25/en?format=image");
put("AKH/Hippo", "https://api.scryfall.com/cards/takh/22/en?format=image");
put("AKH/Insect", "https://api.scryfall.com/cards/takh/19/en?format=image");
put("AKH/Snake", "https://api.scryfall.com/cards/takh/23/en?format=image");
put("AKH/Warrior", "https://api.scryfall.com/cards/takh/17/en?format=image");
put("AKH/Wurm", "https://api.scryfall.com/cards/takh/24/en?format=image");
put("AKH/Zombie", "https://api.scryfall.com/cards/takh/20/en?format=image");
//AKH - embalm ability (token from card)
put("AKH/Angel of Sanctions", "https://api.scryfall.com/cards/takh/1/en?format=image");
put("AKH/Anointer Priest", "https://api.scryfall.com/cards/takh/2/en?format=image");
put("AKH/Aven Initiate", "https://api.scryfall.com/cards/takh/3/en?format=image");
put("AKH/Aven Wind Guide", "https://api.scryfall.com/cards/takh/4/en?format=image");
put("AKH/Glyph Keeper", "https://api.scryfall.com/cards/takh/5/en?format=image");
put("AKH/Heart-Piercer Manticore", "https://api.scryfall.com/cards/takh/6/en?format=image");
put("AKH/Honored Hydra", "https://api.scryfall.com/cards/takh/7/en?format=image");
put("AKH/Labyrinth Guardian", "https://api.scryfall.com/cards/takh/8/en?format=image");
put("AKH/Oketra's Attendant", "https://api.scryfall.com/cards/takh/9/en?format=image");
put("AKH/Sacred Cat", "https://api.scryfall.com/cards/takh/10/en?format=image");
put("AKH/Tah-Crop Skirmisher", "https://api.scryfall.com/cards/takh/11/en?format=image");
put("AKH/Temmet, Vizier of Naktamun", "https://api.scryfall.com/cards/takh/12/en?format=image");
put("AKH/Trueheart Duelist", "https://api.scryfall.com/cards/takh/13/en?format=image");
put("AKH/Unwavering Initiate", "https://api.scryfall.com/cards/takh/14/en?format=image");
put("AKH/Vizier of Many Faces", "https://api.scryfall.com/cards/takh/15/en?format=image");
//AER
put("AER/Etherium Cell", "https://api.scryfall.com/cards/taer/3/en?format=image");
@ -350,6 +367,20 @@ public class ScryfallImageSupportTokens {
put("THB/Wolf", "https://api.scryfall.com/cards/tthb/11/en?format=image");
put("THB/Zombie", "https://api.scryfall.com/cards/tthb/7/en?format=image");
// IKO
put("IKO/Emblem Narset Of The Ancient Way", "https://api.scryfall.com/cards/tiko/12/en?format=image");
put("IKO/Beast", "https://api.scryfall.com/cards/tiko/10/en?format=image");
put("IKO/Cat Bird", "https://api.scryfall.com/cards/tiko/2/en?format=image");
put("IKO/Cat", "https://api.scryfall.com/cards/tiko/1/en?format=image");
put("IKO/Dinosaur Beast", "https://api.scryfall.com/cards/tiko/11/en?format=image");
put("IKO/Dinosaur", "https://api.scryfall.com/cards/tiko/8/en?format=image");
put("IKO/Feather", "https://api.scryfall.com/cards/tiko/9/en?format=image");
put("IKO/Human Soldier/1", "https://api.scryfall.com/cards/tiko/3/en?format=image");
put("IKO/Human Soldier/2", "https://api.scryfall.com/cards/tiko/4/en?format=image");
put("IKO/Human Soldier/3", "https://api.scryfall.com/cards/tiko/5/en?format=image");
put("IKO/Kraken", "https://api.scryfall.com/cards/tiko/6/en?format=image");
put("IKO/Shark", "https://api.scryfall.com/cards/tiko/7/en?format=image");
// PCA (planes)
put("PCA/Eldrazi", "https://api.scryfall.com/cards/tpca/1/en?format=image");
put("PCA/Plane - Academy at Tolaria West", "https://api.scryfall.com/cards/opca/9/en?format=image");
@ -374,6 +405,27 @@ public class ScryfallImageSupportTokens {
put("PCA/Plane - Turri Island", "https://api.scryfall.com/cards/opca/82/en?format=image");
put("PCA/Plane - Undercity Reaches", "https://api.scryfall.com/cards/opca/83/en?format=image");
// C20
put("C20/Angel", "https://api.scryfall.com/cards/tc20/1/en?format=image");
put("C20/Beast", "https://api.scryfall.com/cards/tc20/11/en?format=image");
put("C20/Bird Illusion", "https://api.scryfall.com/cards/tc20/7/en?format=image");
put("C20/Bird", "https://api.scryfall.com/cards/tc20/2/en?format=image");
put("C20/Dinosaur Cat", "https://api.scryfall.com/cards/tc20/16/en?format=image");
put("C20/Drake", "https://api.scryfall.com/cards/tc20/8/en?format=image");
put("C20/Elemental/1", "https://api.scryfall.com/cards/tc20/10/en?format=image"); // 3/1
put("C20/Elemental/2", "https://api.scryfall.com/cards/tc20/3/en?format=image"); // 4/4
put("C20/Goblin Warrior", "https://api.scryfall.com/cards/tc20/17/en?format=image");
put("C20/Human", "https://api.scryfall.com/cards/tc20/4/en?format=image");
put("C20/Hydra", "https://api.scryfall.com/cards/tc20/12/en?format=image");
put("C20/Insect/1", "https://api.scryfall.com/cards/tc20/13/en?format=image"); // deathtouch
put("C20/Insect/2", "https://api.scryfall.com/cards/tc20/18/en?format=image"); // haste
put("C20/Saproling", "https://api.scryfall.com/cards/tc20/14/en?format=image");
put("C20/Snake", "https://api.scryfall.com/cards/tc20/15/en?format=image");
put("C20/Soldier", "https://api.scryfall.com/cards/tc20/5/en?format=image");
put("C20/Spirit", "https://api.scryfall.com/cards/tc20/6/en?format=image");
put("C20/Treasure", "https://api.scryfall.com/cards/tc20/19/en?format=image");
put("C20/Zombie", "https://api.scryfall.com/cards/tc20/9/en?format=image");
// generate supported sets
supportedSets.clear();
for (String cardName : this.keySet()) {

View file

@ -47,7 +47,7 @@ public final class ImageCache {
private static final SoftValuesLoadingCache<String, BufferedImage> FACE_IMAGE_CACHE;
/**
* Common pattern for keys. Format: "<cardname>#<setname>#<collectorID>"
* Common pattern for keys. See ImageCache.getKey for structure info
*/
private static final Pattern KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)#(.*)#(.*)#(.*)");
@ -84,13 +84,21 @@ public final class ImageCache {
boolean cardback = false;
String path;
if (collectorId.isEmpty() || "0".equals(collectorId)) {
if (collectorId.isEmpty() || "0".equals(collectorId) || !tokenDescriptor.isEmpty()) { // tokenDescriptor for embalm ability
info.setToken(true);
path = CardImageUtils.generateTokenImagePath(info);
if (path == null) {
cardback = true;
// TODO: replace empty token by other default card, not cardback
path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
// try token image from card
CardDownloadData newInfo = new CardDownloadData(info);
newInfo.setToken(false);
path = CardImageUtils.buildImagePathToCard(newInfo);
TFile tokenFile = getTFile(path);
if (tokenFile == null || !tokenFile.exists()) {
// token empty token image
// TODO: replace empty token by other default card, not cardback
path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
}
}
} else {
path = CardImageUtils.buildImagePathToCard(info);
@ -245,12 +253,20 @@ public final class ImageCache {
CardDownloadData info = new CardDownloadData(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor);
String path;
if (collectorId.isEmpty() || "0".equals(collectorId)) {
if (collectorId.isEmpty() || "0".equals(collectorId) || !tokenDescriptor.isEmpty()) { // tokenDescriptor for embalm ability
info.setToken(true);
path = CardImageUtils.generateFullTokenImagePath(info);
path = CardImageUtils.generateTokenImagePath(info);
if (path == null) {
// TODO: replace empty token by other default card, not cardback
path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
// try token image from card
CardDownloadData newInfo = new CardDownloadData(info);
newInfo.setToken(false);
path = CardImageUtils.buildImagePathToCard(newInfo);
TFile tokenFile = getTFile(path);
if (tokenFile == null || !tokenFile.exists()) {
// token empty token image
// TODO: replace empty token by other default card, not cardback
path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
}
}
} else {
path = CardImageUtils.buildImagePathToCard(info);
@ -428,11 +444,14 @@ public final class ImageCache {
* Returns the map key for a card, without any suffixes for the image size.
*/
private static String getKey(CardView card, String name, String suffix) {
return name + '#' + card.getExpansionSetCode() + '#' + card.getType() + '#' + card.getCardNumber() + '#'
+ (card.getTokenSetCode() == null ? "" : card.getTokenSetCode())
return name
+ '#' + card.getExpansionSetCode()
+ '#' + card.getType()
+ '#' + card.getCardNumber()
+ '#' + (card.getTokenSetCode() == null ? "" : card.getTokenSetCode())
+ suffix
+ (card.getUsesVariousArt() ? "#usesVariousArt" : "")
+ (card.getTokenDescriptor() != null ? '#' + card.getTokenDescriptor() : "#");
+ '#' + (card.getTokenDescriptor() != null ? card.getTokenDescriptor() : "");
}
/**

View file

@ -101,6 +101,7 @@
|Generate|EMBLEM:DOM|Teferi, Hero of Dominaria||Emblem Teferi|TeferiHeroOfDominariaEmblem|
|Generate|EMBLEM:AER|Tezzeret the Schemer||Emblem Tezzeret|TezzeretTheSchemerEmblem|
|Generate|EMBLEM:ELD|Garruk, Cursed Huntsman||Emblem Garruk|GarrukCursedHuntsmanEmblem|
|Generate|EMBLEM:IKO|Narset Of The Ancient Way||Emblem Narset|NarsetOfTheAncientWayEmblem|
|Generate|PLANE:PCA|Plane - Academy at Tolaria West|||AcademyAtTolariaWestPlane|
|Generate|PLANE:PCA|Plane - Agyrem|||AgyremPlane|
|Generate|PLANE:PCA|Plane - Akoum|||AkoumPlane|
@ -153,7 +154,7 @@
|Generate|TOK:5ED|Serf|||SerfToken|
|Generate|TOK:5ED|Snake|||SerpentGeneratorSnakeToken|
|Generate|TOK:5ED|Thrull|||BreedingPitBlackInsectToken|
|Generate|TOK:6ED|Cat|||WaitingInTheWeedsCatToken|
|Generate|TOK:6ED|Cat|||GreenCatToken|
|Generate|TOK:6ED|Citizen|||CitizenToken|
|Generate|TOK:6ED|Djinn|||DjinnToken|
|Generate|TOK:6ED|Goblin|||GoblinToken|
@ -167,30 +168,34 @@
|Generate|TOK:AER|Etherium Cell|||EtheriumCellToken|
|Generate|TOK:AER|Gremlin|||GremlinToken|
|Generate|TOK:AER|Ragavan|||RagavanToken|
# AKH
|Generate|TOK:AKH|Beast|||BeastToken3|
|Generate|TOK:AKH|Cat|||CatToken2|
|Generate|TOK:AKH|Drake|||DrakeToken|
|Generate|TOK:AKH|Hippo|||HippoToken2|
|Generate|TOK:AKH|Insect|||NestOfScarabsBlackInsectToken|
|Generate|TOK:AKH|Snake|||DeathtouchSnakeToken|
|Generate|TOK:AKH|Warrior|||WarriorVigilantToken|
|Generate|TOK:AKH|Wurm|||WurmToken3|
|Generate|TOK:AKH|Zombie|||ZombieToken
|Generate|TOK:AKH|Wurm|||Wurm55Token|
|Generate|TOK:AKH|Zombie|||ZombieToken|
#TOK:AKH - some tokens from real cards (see Embalm ability)
#|Generate|TOK:AKH|Angel of Sanctions||
#|Generate|TOK:AKH|Anointer Priest||
#|Generate|TOK:AKH|Aven Initiate||
#|Generate|TOK:AKH|Aven Wind Guide||
#|Generate|TOK:AKH|Glyph Keeper||
#|Generate|TOK:AKH|Heart-Piercer Manticore||
#|Generate|TOK:AKH|Honored Hydra||
#|Generate|TOK:AKH|Labyrinth Guardian||
#|Generate|TOK:AKH|Oketra's Attendant||
#|Generate|TOK:AKH|Sacred Cat||
#|Generate|TOK:AKH|Tah-Crop Skirmisher||
#|Generate|TOK:AKH|Temmet, Vizier of Naktamun||
#|Generate|TOK:AKH|Trueheart Duelist||
#|Generate|TOK:AKH|Unwavering Initiate||
#|Generate|TOK:AKH|Vizier of Many Faces||
|Generate|TOK:AKH|Angel of Sanctions||
|Generate|TOK:AKH|Anointer Priest||
|Generate|TOK:AKH|Aven Initiate||
|Generate|TOK:AKH|Aven Wind Guide||
|Generate|TOK:AKH|Glyph Keeper||
|Generate|TOK:AKH|Heart-Piercer Manticore||
|Generate|TOK:AKH|Honored Hydra||
|Generate|TOK:AKH|Labyrinth Guardian||
|Generate|TOK:AKH|Oketra's Attendant||
|Generate|TOK:AKH|Sacred Cat||
|Generate|TOK:AKH|Tah-Crop Skirmisher||
|Generate|TOK:AKH|Temmet, Vizier of Naktamun||
|Generate|TOK:AKH|Trueheart Duelist||
|Generate|TOK:AKH|Unwavering Initiate||
|Generate|TOK:AKH|Vizier of Many Faces||
|Generate|TOK:ALA|Beast|||GodSireBeastToken|
|Generate|TOK:ALA|Dragon|||DragonToken|
|Generate|TOK:ALA|Goblin|||GoblinTokenWithHaste|
@ -269,9 +274,9 @@
|Generate|TOK:C13|Beast|||OneDozenEyesBeastToken|
|Generate|TOK:C13|Beast|||SpawningGroundsBeastToken|
|Generate|TOK:C13|Drake|||LeafdrakeRoostDrakeToken|
|Generate|TOK:C13|Elemental|||SeedGuardianToken|
|Generate|TOK:C13|Elemental|||WalkerOfTheGroveToken|
|Generate|TOK:C13|Elemental|||YoungPyromancerElementalToken|
|Generate|TOK:C13|Elemental|1||SeedGuardianToken|
|Generate|TOK:C13|Elemental|2||WalkerOfTheGroveToken|
|Generate|TOK:C13|Elemental|3||RedElementalToken|
|Generate|TOK:C13|Elephant|||ElephantToken|
|Generate|TOK:C13|Elf Warrior|||ElfToken|
|Generate|TOK:C13|Goat|||GoatToken|
@ -318,8 +323,8 @@
|Generate|TOK:C14|Treefolk|||SylvanOfferingTreefolkToken|
|Generate|TOK:C14|Whale|||ReefWormWhaleToken|
|Generate|TOK:C14|Wolf|||WolfToken|
|Generate|TOK:C14|Wurm|1||Wurm1Token|
|Generate|TOK:C14|Wurm|2||Wurm2Token|
|Generate|TOK:C14|Wurm|1||WurmWithDeathtouchToken|
|Generate|TOK:C14|Wurm|2||WurmWithLifelinkToken|
|Generate|TOK:C14|Zombie|1||ZombieToken|
|Generate|TOK:C14|Zombie|2||StitcherGeralfZombieToken|
|Generate|TOK:C15|Angel|||AngelToken|
@ -350,7 +355,7 @@
|Generate|TOK:C16|Beast||
|Generate|TOK:C16|Bird|1|
|Generate|TOK:C16|Bird|2|
|Generate|TOK:C16|Elemental||
|Generate|TOK:C16|Elemental|||WhiteElementalToken|
|Generate|TOK:C16|Elf Warrior||
|Generate|TOK:C16|Germ||
|Generate|TOK:C16|Goat||
@ -368,7 +373,7 @@
|Generate|TOK:C16|Worm||
|Generate|TOK:C16|Zombie||
|Generate|TOK:C17|Bat||
|Generate|TOK:C17|Cat||
|Generate|TOK:C17|Cat|||CatToken|
|Generate|TOK:C17|Cat Dragon|||WasitoraCatDragonToken|
|Generate|TOK:C17|Cat Warrior||
|Generate|TOK:C17|Dragon|1||DragonToken|
@ -566,11 +571,11 @@
|Generate|TOK:DGM|Rhino|||RhinoToken|
|Generate|TOK:DGM|Soldier|||SoldierTokenWithHaste|
|Generate|TOK:DGM|Spirit|||TeysaEnvoyOfGhostsToken|
|Generate|TOK:DGM|Wurm|||WurmToken2|
|Generate|TOK:DGM|Wurm|||WurmToken3|
|Generate|TOK:DGM|Wurm|1||WurmWithTrampleToken|
|Generate|TOK:DGM|Wurm|2||Wurm55Token|
|Generate|TOK:DIS|Bird|||DovescapeToken|
|Generate|TOK:DIS|Drake|||LeafdrakeRoostDrakeToken|
|Generate|TOK:DIS|Elemental|||ElementalToken|
|Generate|TOK:DIS|Elemental|||ResearchDevelopmentToken|
|Generate|TOK:DIS|Goblin|||RakdosGuildmageGoblinToken|
|Generate|TOK:DIS|Saproling|||SaprolingToken|
|Generate|TOK:DIS|Snake|||PatagiaViperSnakeToken|
@ -618,7 +623,7 @@
|Generate|TOK:EMA|Beast|||CarnivoreToken|
|Generate|TOK:EMA|Carnivore||
|Generate|TOK:EMA|Dragon|||DragonEggDragonToken|
|Generate|TOK:EMA|Elemental|1||YoungPyromancerElementalToken|
|Generate|TOK:EMA|Elemental|1||RedElementalToken|
|Generate|TOK:EMA|Elemental|2||CallTheSkyBreakerElementalToken|
|Generate|TOK:EMA|Elephant|||ElephantToken|
|Generate|TOK:EMA|Elf Warrior|||ElfToken|
@ -654,7 +659,7 @@
|Generate|TOK:EVE|Spirit|||BeckonApparitionToken|
|Generate|TOK:EVE|Wolf|||WolfToken|
|Generate|TOK:EVE|Worm|||WormHarvestToken|
|Generate|TOK:EVG|Elemental||
|Generate|TOK:EVG|Elemental|||VoiceOfTheWoodsElementalToken|
|Generate|TOK:EVG|Elf Warrior|||ElfToken|
|Generate|TOK:EVG|Goblin|||GoblinToken|
|Generate|TOK:EXO|Pegasus|||PegasusToken|
@ -790,8 +795,8 @@
|Generate|TOK:LRW|Avatar|||AvatarToken|
|Generate|TOK:LRW|Beast|||BeastToken|
|Generate|TOK:LRW|Elemental Shaman|||ElementalShamanToken|
|Generate|TOK:LRW|Elemental|||WalkerOfTheGroveToken|
|Generate|TOK:LRW|Elemental|||WhiteElementalToken|
|Generate|TOK:LRW|Elemental|1||WalkerOfTheGroveToken|
|Generate|TOK:LRW|Elemental|2||WhiteElementalToken|
|Generate|TOK:LRW|Elf Warrior|||ElfToken|
|Generate|TOK:LRW|Goblin Rogue|||GoblinRogueToken|
|Generate|TOK:LRW|Kithkin Soldier|||KithkinToken|
@ -833,8 +838,8 @@
|Generate|TOK:M14|Beast|||BeastToken|
|Generate|TOK:M14|Cat|||CatToken|
|Generate|TOK:M14|Dragon|||DragonEggDragonToken|
|Generate|TOK:M14|Elemental|1||YoungPyromancerElementalToken|
|Generate|TOK:M14|Elemental|2||YoungPyromancerElementalToken|
|Generate|TOK:M14|Elemental|1||RedElementalToken|
|Generate|TOK:M14|Elemental|2||RedElementalToken|
|Generate|TOK:M14|Goat|||GoatToken|
|Generate|TOK:M14|Saproling|||SaprolingToken|
|Generate|TOK:M14|Sliver||
@ -908,7 +913,7 @@
|Generate|TOK:MM3|Bird||
|Generate|TOK:MM3|Centaur||
|Generate|TOK:MM3|Dragon||
|Generate|TOK:MM3|Elemental||
|Generate|TOK:MM3|Elemental|||VoiceOfResurgenceToken|
|Generate|TOK:MM3|Elephant||
|Generate|TOK:MM3|Giant Warrior||
|Generate|TOK:MM3|Goblin Warrior||
@ -955,7 +960,7 @@
|Generate|TOK:MOR|Wolf|||WolfToken|
|Generate|TOK:MRD|Beast|||OneDozenEyesBeastToken|
|Generate|TOK:MRD|Demon|||ReignOfThePitToken|
|Generate|TOK:MRD|Elemental|||ElementalToken|
|Generate|TOK:MRD|Elemental|||ElementalTokenWithHaste|
|Generate|TOK:MRD|Insect|||InsectToken|
|Generate|TOK:MRD|Myr|||MyrToken|
|Generate|TOK:MRD|Pentavite|||PentaviteToken|
@ -989,7 +994,7 @@
|Generate|TOK:OGW|Eldrazi Scion|5||EldraziScionToken|
|Generate|TOK:OGW|Eldrazi Scion|6||EldraziScionToken|
|Generate|TOK:OGW|Elemental|1||SeedGuardianToken|
|Generate|TOK:OGW|Elemental|2||ElementalToken|
|Generate|TOK:OGW|Elemental|2||ElementalTokenWithHaste|
|Generate|TOK:OGW|Knight Ally|||KnightAllyToken|
|Generate|TOK:OGW|Kor Ally|||KorAllyToken|
|Generate|TOK:OGW|Octopus|||OctopusToken|
@ -1062,21 +1067,21 @@
|Generate|TOK:RTR|Bird|||BirdToken|
|Generate|TOK:RTR|Centaur|||CentaurToken|
|Generate|TOK:RTR|Dragon|||UtvaraHellkiteDragonToken|
|Generate|TOK:RTR|Elemental||
|Generate|TOK:RTR|Elemental|||GreenAndWhiteElementalToken|
|Generate|TOK:RTR|Goblin|||GoblinToken|
|Generate|TOK:RTR|Knight|||KnightToken|
|Generate|TOK:RTR|Ooze|||MysticGenesisOozeToken|
|Generate|TOK:RTR|Rhino|||RhinoToken|
|Generate|TOK:RTR|Saproling|||SaprolingToken|
|Generate|TOK:RTR|Soldier|||SoldierToken|
|Generate|TOK:RTR|Wurm|||WurmToken2|
|Generate|TOK:RTR|Wurm|||WurmWithTrampleToken|
|Generate|TOK:SCG|Angel|||AngelToken|
|Generate|TOK:SCG|Beast|||BeastToken2|
|Generate|TOK:SCG|Dragon|||DragonToken2|
|Generate|TOK:SCG|Goblin|||GoblinToken|
|Generate|TOK:SCG|Soldier|||SoldierToken|
|Generate|TOK:SHM|Elemental|1||DinOfTheFireherdToken|
|Generate|TOK:SHM|Elemental|2||YoungPyromancerElementalToken|
|Generate|TOK:SHM|Elemental|2||RedElementalToken|
|Generate|TOK:SHM|Elf Warrior|1|
|Generate|TOK:SHM|Elf Warrior|2|
|Generate|TOK:SHM|Elf Warrior|||ElfToken|
@ -1105,7 +1110,7 @@
|Generate|TOK:SOI|Vampire Knight|||VampireKnightToken|
|Generate|TOK:SOI|Wolf|||WolfToken|
|Generate|TOK:SOI|Zombie|||ZombieToken|
|Generate|TOK:SOK|Elemental|||ElementalToken|
|Generate|TOK:SOK|Elemental|||ElementalTokenWithHaste|
|Generate|TOK:SOK|Snake|||SnakeToken|
|Generate|TOK:SOK|Spirit|||SpiritToken|
|Generate|TOK:SOK|Urami|||UramiToken|
@ -1117,8 +1122,8 @@
|Generate|TOK:SOM|Myr|||MyrToken|
|Generate|TOK:SOM|Soldier|||SoldierToken|
|Generate|TOK:SOM|Wolf|||WolfToken|
|Generate|TOK:SOM|Wurm|1||Wurm1Token|
|Generate|TOK:SOM|Wurm|2||Wurm2Token|
|Generate|TOK:SOM|Wurm|1||WurmWithDeathtouchToken|
|Generate|TOK:SOM|Wurm|2||WurmWithLifelinkToken|
|Generate|TOK:STH|Goblin|||GoblinToken|
|Generate|TOK:STH|Insect|||HornetToken|
|Generate|TOK:STH|Insect|||WaspToken|
@ -1146,7 +1151,7 @@
|Generate|TOK:THS|Soldier|1||SoldierToken|
|Generate|TOK:THS|Soldier|2||SoldierToken|
|Generate|TOK:TMP|Beast|||CarnivoreToken|
|Generate|TOK:TMP|Hound|||HoundToken|
|Generate|TOK:TMP|Dog|||GreenDogToken|
|Generate|TOK:TMP|Pegasus|||PegasusToken|
|Generate|TOK:TMP|Reflection|||ReflectionToken|
|Generate|TOK:TMP|Saproling|||SaprolingToken|
@ -1213,8 +1218,8 @@
|Generate|TOK:VMA|Spirit|||SpiritWhiteToken|
|Generate|TOK:VMA|Squirrel|||SquirrelToken|
|Generate|TOK:VMA|Thopter|||ThopterColorlessToken|
|Generate|TOK:VMA|Wurm|||PenumbraWurmToken|
|Generate|TOK:VMA|Wurm|||WurmToken|
|Generate|TOK:VMA|Wurm|1||PenumbraWurmToken|
|Generate|TOK:VMA|Wurm|2||WurmToken|
|Generate|TOK:VMA|Zombie|||ZombieToken|
|Generate|TOK:WTH|Squirrel|||SquirrelToken|
|Generate|TOK:WWK|Construct|||StoneTrapIdolToken|
@ -1238,8 +1243,8 @@
|Generate|TOK:ZEN|Angel|||AngelToken|
|Generate|TOK:ZEN|Beast|||BeastToken2|
|Generate|TOK:ZEN|Bird|||BirdToken|
|Generate|TOK:ZEN|Elemental||
|Generate|TOK:ZEN|Elemental|||ZektarShrineElementalToken|
|Generate|TOK:ZEN|Elemental|1||RedElementalWithTrampleAndHaste|
|Generate|TOK:ZEN|Elemental|2||RedElementalWithTrampleAndHaste|
|Generate|TOK:ZEN|Illusion|||IllusionToken|
|Generate|TOK:ZEN|Kor Soldier|||KorSoldierToken|
|Generate|TOK:ZEN|Merfolk||
@ -1268,6 +1273,7 @@
|Generate|TOK:GRN|Soldier|||SoldierLifelinkToken|
|Generate|TOK:WAR|Angel|||AngelVigilanceToken|
|Generate|TOK:WAR|Assassin|||AssassinToken2|
|Generate|TOK:WAR|Citizen|||PlanewideCelebrationToken|
|Generate|TOK:WAR|Devil|||DevilToken|
|Generate|TOK:WAR|Dragon|||DragonToken|
|Generate|TOK:WAR|Goblin|||GoblinToken|
@ -1288,7 +1294,7 @@
|Generate|TOK:MH1|Bear|||BearToken|
|Generate|TOK:MH1|Bird|||BirdToken|
|Generate|TOK:MH1|Construct|||KarnConstructToken|
|Generate|TOK:MH1|Elemental|1||YoungPyromancerElementalToken|
|Generate|TOK:MH1|Elemental|1||RedElementalToken|
|Generate|TOK:MH1|Elemental|2||AkoumStonewakerElementalToken|
|Generate|TOK:MH1|Elephant|||ElephantToken|
|Generate|TOK:MH1|Goblin|||GoblinToken|
@ -1318,7 +1324,7 @@
|Generate|TOK:M19|Zombie|||ZombieToken|
|Generate|TOK:M20|Ajani's Pridemate|||AjanisPridemateToken|
|Generate|TOK:M20|Demon|||DemonToken|
|Generate|TOK:M20|Elemental|||YoungPyromancerElementalToken|
|Generate|TOK:M20|Elemental|||RedElementalToken|
|Generate|TOK:M20|Elemental Bird|||MuYanlingSkyDancerToken|
|Generate|TOK:M20|Golem|||GolemToken|
|Generate|TOK:M20|Soldier|||SoldierToken|
@ -1326,6 +1332,8 @@
|Generate|TOK:M20|Treasure|||TreasureToken|
|Generate|TOK:M20|Wolf|||WolfToken|
|Generate|TOK:M20|Zombie|||ZombieToken|
# ELD
|Generate|TOK:ELD|Bear|||BearToken|
|Generate|TOK:ELD|Boar|||WolfsQuarryToken|
|Generate|TOK:ELD|Dwarf|||DwarfToken|
@ -1344,6 +1352,8 @@
|Generate|TOK:ELD|Mouse|||MouseToken|
|Generate|TOK:ELD|Rat|||RatToken|
|Generate|TOK:ELD|Wolf|||GarrukCursedHuntsmanToken|
# THB
|Generate|TOK:THB|Goat|||GoatToken|
|Generate|TOK:THB|Human Soldier|||HumanSoldierToken|
|Generate|TOK:THB|Pegasus|||PegasusToken2|
@ -1356,5 +1366,42 @@
|Generate|TOK:THB|Spider|||SpiderToken|
|Generate|TOK:THB|Wolf|||WolfToken|
|Generate|TOK:THB|Nightmare|||AshiokNightmareMuseToken|
|Generate|TOK:THB|GoldToken|||Gold|
|Generate|TOK:THB|ArtifactWallToken|||Wall|
|Generate|TOK:THB|Gold|||GoldToken|
|Generate|TOK:THB|Wall|||ArtifactWallToken|
# IKO
|Generate|TOK:IKO|Beast|||BeastToken|
|Generate|TOK:IKO|Cat Bird|||CatBirdToken|
|Generate|TOK:IKO|Cat|||CatToken2|
|Generate|TOK:IKO|Dinosaur Beast|||DinosaurBeastToken|
|Generate|TOK:IKO|Dinosaur|||DinosaurHasteToken|
|Generate|TOK:IKO|Feather|||FeatherToken|
|Generate|TOK:IKO|Human Soldier|1||HumanSoldierToken|
|Generate|TOK:IKO|Human Soldier|2||HumanSoldierToken|
|Generate|TOK:IKO|Human Soldier|3||HumanSoldierToken|
|Generate|TOK:IKO|Kraken|||KrakenToken|
|Generate|TOK:IKO|Shark|||SharkToken|
# C20
|Generate|TOK:C20|Angel|||AngelToken|
|Generate|TOK:C20|Beast|||BeastToken2|
|Generate|TOK:C20|Bird|||BirdToken|
|Generate|TOK:C20|Bird Illusion|||BirdIllusionToken|
|Generate|TOK:C20|Dinosaur Cat|||DinosaurCatToken|
|Generate|TOK:C20|Drake|||DrakeToken|
|Generate|TOK:C20|Elemental|1||ElementalTokenWithHaste|
|Generate|TOK:C20|Elemental|2||WhiteElementalToken|
|Generate|TOK:C20|Goblin Warrior|||GoblinWarriorToken|
|Generate|TOK:C20|Human|||HumanToken|
|Generate|TOK:C20|Hydra|||ZaxaraTheExemplaryHydraToken|
|Generate|TOK:C20|Insect|1||HornetQueenInsectToken|
|Generate|TOK:C20|Insect|2||TheLocustGodInsectToken|
|Generate|TOK:C20|Saproling|||SaprolingToken|
|Generate|TOK:C20|Snake|||SnakeToken|
|Generate|TOK:C20|Soldier|||SoldierToken|
|Generate|TOK:C20|Spirit|||SpiritWhiteToken|
|Generate|TOK:C20|Treasure|||TreasureToken|
|Generate|TOK:C20|Zombie|||ZombieToken|
# JMP
|Generate|TOK:JMP|Unicorn|||UnicornToken|

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-root</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-common</artifactId>

View file

@ -16,7 +16,6 @@ import mage.utils.MageVersion;
import mage.view.*;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@ -84,7 +83,7 @@ public interface MageServer {
boolean isTableOwner(String sessionId, UUID roomId, UUID tableId) throws MageException;
Optional<TableView> getTable(UUID roomId, UUID tableId) throws MageException;
TableView getTable(UUID roomId, UUID tableId) throws MageException;
List<TableView> getTables(UUID roomId) throws MageException;
@ -95,13 +94,13 @@ public interface MageServer {
void leaveChat(UUID chatId, String sessionId) throws MageException;
Optional<UUID> getTableChatId(UUID tableId) throws MageException;
UUID getTableChatId(UUID tableId) throws MageException;
Optional<UUID> getGameChatId(UUID gameId) throws MageException;
UUID getGameChatId(UUID gameId) throws MageException;
Optional<UUID> getRoomChatId(UUID roomId) throws MageException;
UUID getRoomChatId(UUID roomId) throws MageException;
Optional<UUID> getTournamentChatId(UUID tournamentId) throws MageException;
UUID getTournamentChatId(UUID tournamentId) throws MageException;
//room methods
UUID getMainRoomId() throws MageException;

View file

@ -42,6 +42,7 @@ public class Connection {
// private UserSkipPrioritySteps userSkipPrioritySteps;
private static final String serialization = "?serializationtype=jboss";
private static final String transport = "bisocket";
private static final String threadpool = "onewayThreadPool=mage.remote.CustomThreadPool";
private final String parameter;
@ -78,13 +79,13 @@ public class Connection {
try {
InetAddress inet = getLocalAddress();
if (inet != null) {
return transport + "://" + inet.getHostAddress() + ':' + port + '/' + serialization + parameter;
return transport + "://" + inet.getHostAddress() + ':' + port + '/' + serialization + "&" + threadpool + parameter;
}
} catch (SocketException ex) {
// just use localhost if can't find local ip
}
}
return transport + "://" + host + ':' + port + '/' + serialization + parameter;
return transport + "://" + host + ':' + port + '/' + serialization + "&" + threadpool + parameter;
}
public ProxyType getProxyType() {

View file

@ -0,0 +1,32 @@
package mage.remote;
import java.lang.reflect.Field;
import java.util.concurrent.ThreadPoolExecutor;
import org.apache.log4j.Logger;
import org.jboss.util.threadpool.BasicThreadPool;
public class CustomThreadPool extends BasicThreadPool {
private static final Logger logger = Logger.getLogger(SessionImpl.class);
@Override
public void setMaximumPoolSize(int size) {
/*
* I really don't want to implement a whole new threadpool
* just to fix this and the executor is private
*/
try {
Field executorField = BasicThreadPool.class.getField("executor");
executorField.setAccessible(true);
ThreadPoolExecutor executor = (ThreadPoolExecutor) executorField.get(this);
synchronized (executor) {
executor.setMaximumPoolSize(size);
executor.setCorePoolSize(size);
}
} catch (NoSuchFieldException | SecurityException e) {
logger.error("Failed to get field executor from BasicThreadPool", e);
} catch (IllegalArgumentException | IllegalAccessException e) {
logger.error("Failed to get executor object from BasicThreadPool", e);
}
}
}

View file

@ -647,7 +647,7 @@ public class SessionImpl implements Session {
public Optional<UUID> getRoomChatId(UUID roomId) {
try {
if (isConnected()) {
return server.getRoomChatId(roomId);
return Optional.of(server.getRoomChatId(roomId));
}
} catch (MageException ex) {
handleMageException(ex);
@ -659,7 +659,7 @@ public class SessionImpl implements Session {
public Optional<UUID> getTableChatId(UUID tableId) {
try {
if (isConnected()) {
return server.getTableChatId(tableId);
return Optional.of(server.getTableChatId(tableId));
}
} catch (MageException ex) {
handleMageException(ex);
@ -671,7 +671,7 @@ public class SessionImpl implements Session {
public Optional<UUID> getGameChatId(UUID gameId) {
try {
if (isConnected()) {
return server.getGameChatId(gameId);
return Optional.of(server.getGameChatId(gameId));
}
} catch (MageException ex) {
handleMageException(ex);
@ -685,7 +685,7 @@ public class SessionImpl implements Session {
public Optional<TableView> getTable(UUID roomId, UUID tableId) {
try {
if (isConnected()) {
return server.getTable(roomId, tableId);
return Optional.of(server.getTable(roomId, tableId));
}
} catch (MageException ex) {
handleMageException(ex);
@ -829,7 +829,7 @@ public class SessionImpl implements Session {
public Optional<UUID> getTournamentChatId(UUID tournamentId) {
try {
if (isConnected()) {
return server.getTournamentChatId(tournamentId);
return Optional.of(server.getTournamentChatId(tournamentId));
}
} catch (MageException ex) {
handleMageException(ex);

View file

@ -11,11 +11,11 @@ public class MageVersion implements Serializable, Comparable<MageVersion> {
public static final int MAGE_VERSION_MAJOR = 1;
public static final int MAGE_VERSION_MINOR = 4;
public static final int MAGE_VERSION_PATCH = 42;
public static final int MAGE_VERSION_PATCH = 43;
public static final String MAGE_EDITION_INFO = ""; // set "-beta2" for 1.4.32V1-beta2
public static final String MAGE_VERSION_MINOR_PATCH = "V7"; // default
public static final String MAGE_VERSION_MINOR_PATCH = "V0"; // default
// strict mode
private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = true; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes)
private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = false; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes)
public static final boolean MAGE_VERSION_SHOW_BUILD_TIME = true;
private final int major;

View file

@ -1,6 +1,7 @@
package mage.view;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import java.io.Serializable;
import java.util.LinkedHashMap;
@ -28,15 +29,32 @@ public class AbilityPickerView implements Serializable {
if (objectName == null) {
rule = ability.getRule(true);
} else {
rule = ability.getRule(objectName);
if (rule.isEmpty()) {
rule = ability.toString();
// spell abilities must start with "Cast name" (split cards have different names for each spell part)
if (ability instanceof SpellAbility) {
SpellAbility spell = (SpellAbility) ability;
rule = getAbilityRules(spell, spell.getCardName());
if (!rule.startsWith("Cast ")) {
rule = spell.toString() + ": " + rule; // spell.toString() must return this.name (example: Cast Armed)
}
} else {
rule = getAbilityRules(ability, objectName);
}
}
choices.put(ability.getId(), num + ". " + rule);
}
}
private String getAbilityRules(Ability ability, String objectName) {
String rule = ability.getRule(objectName);
if (rule.isEmpty()) {
rule = ability.toString();
}
if (!rule.isEmpty()) {
rule = Character.toUpperCase(rule.charAt(0)) + rule.substring(1);
}
return rule;
}
public AbilityPickerView(Map<UUID, String> modes, String message) {
this.choices = modes;
this.message = message;

View file

@ -1,14 +1,13 @@
package mage.view;
import java.util.ArrayList;
import java.util.EnumSet;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.constants.CardType;
import mage.constants.SuperType;
import mage.util.SubTypeList;
import java.util.ArrayList;
import java.util.EnumSet;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -29,7 +28,7 @@ public class AbilityView extends CardView {
this.power = "";
this.toughness = "";
this.loyalty = "";
this.cardTypes = EnumSet.noneOf(CardType.class);
this.cardTypes = new ArrayList<CardType>();
this.subTypes = new SubTypeList();
this.superTypes = EnumSet.noneOf(SuperType.class);
this.color = new ObjectColor();
@ -45,5 +44,4 @@ public class AbilityView extends CardView {
this.name = name;
}
}

View file

@ -1,6 +1,8 @@
package mage.view;
import com.google.gson.annotations.Expose;
import java.util.*;
import java.util.stream.Collectors;
import mage.MageObject;
import mage.ObjectColor;
import mage.abilities.Abilities;
@ -29,9 +31,6 @@ import mage.target.Targets;
import mage.util.CardUtil;
import mage.util.SubTypeList;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -55,7 +54,7 @@ public class CardView extends SimpleCardView {
@Expose
protected String loyalty = "";
protected String startingLoyalty;
protected Set<CardType> cardTypes;
protected ArrayList<CardType> cardTypes;
protected SubTypeList subTypes;
protected Set<SuperType> superTypes;
protected ObjectColor color;
@ -151,7 +150,7 @@ public class CardView extends SimpleCardView {
this.toughness = cardView.toughness;
this.loyalty = cardView.loyalty;
this.startingLoyalty = cardView.startingLoyalty;
this.cardTypes = new HashSet<>(cardView.cardTypes);
this.cardTypes = new ArrayList<>(cardView.cardTypes);
this.subTypes = new SubTypeList(cardView.subTypes);
this.superTypes = cardView.superTypes;
@ -213,8 +212,8 @@ public class CardView extends SimpleCardView {
* @param card
* @param game
* @param controlled is the card view created for the card controller - used
* for morph / face down cards to know which player may see information for
* the card
* for morph / face down cards to know which player may see information for
* the card
*/
public CardView(Card card, Game game, boolean controlled) {
this(card, game, controlled, false, false);
@ -240,12 +239,12 @@ public class CardView extends SimpleCardView {
/**
* @param card
* @param game
* @param controlled is the card view created for the card controller - used
* for morph / face down cards to know which player may see information for
* the card
* @param controlled is the card view created for the card controller - used
* for morph / face down cards to know which player may see information for
* the card
* @param showFaceDownCard if true and the card is not on the battlefield,
* also a face down card is shown in the view, face down cards will be shown
* @param storeZone if true the card zone will be set in the zone attribute.
* also a face down card is shown in the view, face down cards will be shown
* @param storeZone if true the card zone will be set in the zone attribute.
*/
public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) {
super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), game != null, card.getTokenDescriptor());
@ -318,13 +317,6 @@ public class CardView extends SimpleCardView {
}
}
AdventureCard adventureCard = null;
AdventureCardSpell adventureCardSpell = null;
if (card instanceof AdventureCard) {
adventureCard = (AdventureCard) card;
adventureCardSpell = (AdventureCardSpell) adventureCard.getSpellCard();
}
String fullCardName;
if (splitCard != null) {
this.isSplitCard = true;
@ -340,7 +332,9 @@ public class CardView extends SimpleCardView {
fullCardName = card.getName(); // split card contains full name as normal
this.manaCostLeft = splitCard.getLeftHalfCard().getManaCost().getSymbols();
this.manaCostRight = splitCard.getRightHalfCard().getManaCost().getSymbols();
} else if (adventureCard != null) {
} else if (card instanceof AdventureCard) {
AdventureCard adventureCard = ((AdventureCard) card);
AdventureCardSpell adventureCardSpell = ((AdventureCardSpell) adventureCard.getSpellCard());
fullCardName = adventureCard.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + adventureCardSpell.getName();
this.manaCostLeft = adventureCardSpell.getManaCost().getSymbols();
this.manaCostRight = adventureCard.getManaCost().getSymbols();
@ -467,7 +461,7 @@ public class CardView extends SimpleCardView {
} else if (spell.getCard() != null) {
SplitCard wholeCard = ((SplitCardHalf) spell.getCard()).getParentCard();
Abilities<Ability> aftermathHalfAbilities = wholeCard.getRightHalfCard().getAbilities(game);
if (aftermathHalfAbilities.stream().anyMatch(ability -> ability instanceof AftermathAbility)) {
if (aftermathHalfAbilities.stream().anyMatch(halfAbility -> halfAbility instanceof AftermathAbility)) {
if (ty == SpellAbilityType.SPLIT_RIGHT) {
artRect = ArtRect.AFTERMATH_BOTTOM;
} else {
@ -636,7 +630,7 @@ public class CardView extends SimpleCardView {
this.toughness = "";
this.loyalty = "";
this.startingLoyalty = "";
this.cardTypes = EnumSet.noneOf(CardType.class);
this.cardTypes = new ArrayList<>();
this.subTypes = new SubTypeList();
this.superTypes = EnumSet.noneOf(SuperType.class);
this.color = new ObjectColor();
@ -764,7 +758,7 @@ public class CardView extends SimpleCardView {
return startingLoyalty;
}
public Set<CardType> getCardTypes() {
public ArrayList<CardType> getCardTypes() {
return cardTypes;
}
@ -1030,26 +1024,25 @@ public class CardView extends SimpleCardView {
}
public String getColorText() {
String color = getColor().getDescription();
return color.substring(0, 1).toUpperCase(Locale.ENGLISH) + color.substring(1);
String colorText = getColor().getDescription();
return colorText.substring(0, 1).toUpperCase(Locale.ENGLISH) + colorText.substring(1);
}
public String getTypeText() {
StringBuilder type = new StringBuilder();
StringBuilder typeText = new StringBuilder();
if (!getSuperTypes().isEmpty()) {
type.append(String.join(" ", getSuperTypes().stream().map(SuperType::toString).collect(Collectors.toList())));
type.append(" ");
typeText.append(String.join(" ", getSuperTypes().stream().map(SuperType::toString).collect(Collectors.toList())));
typeText.append(" ");
}
if (!getCardTypes().isEmpty()) {
type.append(String.join(" ", getCardTypes().stream().map(CardType::toString).collect(Collectors.toList())));
type.append(" ");
typeText.append(String.join(" ", getCardTypes().stream().map(CardType::toString).collect(Collectors.toList())));
typeText.append(" ");
}
if (!getSubTypes().isEmpty()) {
type.append(" - ");
type.append(String.join(" ", getSubTypes().stream().map(SubType::toString).collect(Collectors.toList())));
typeText.append(" - ");
typeText.append(String.join(" ", getSubTypes().stream().map(SubType::toString).collect(Collectors.toList())));
}
return type.toString();
return typeText.toString();
}
public boolean isLand() {

View file

@ -95,10 +95,9 @@ public class PermanentView extends CardView {
if (controlled) {
// must be a morphed or manifested card
for (Ability permanentAbility : permanent.getAbilities()) {
if (permanentAbility instanceof TurnFaceUpAbility && !permanentAbility.getRuleVisible()) {
this.rules.add(permanentAbility.getRule(true));
}
if (permanentAbility.getWorksFaceDown()) {
this.rules.add(permanentAbility.getRule(true));
} else if (permanentAbility instanceof TurnFaceUpAbility && !permanentAbility.getRuleVisible()) {
this.rules.add(permanentAbility.getRule());
}
}

View file

@ -22,6 +22,7 @@ public class TournamentTypeView implements Serializable {
private final boolean elimination;
private final boolean random;
private final boolean richMan;
private final boolean jumpstart;
public TournamentTypeView(TournamentType tournamentType) {
this.name = tournamentType.getName();
@ -34,6 +35,7 @@ public class TournamentTypeView implements Serializable {
this.elimination = tournamentType.isElimination();
this.random = tournamentType.isRandom();
this.richMan = tournamentType.isRichMan();
this.jumpstart = tournamentType.isJumpstart();
}
@Override
@ -80,4 +82,9 @@ public class TournamentTypeView implements Serializable {
public boolean isRichMan() {
return richMan;
}
public boolean isJumpstart() {
return jumpstart;
}
}

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-counter-plugin</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-root</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-plugins</artifactId>

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-root</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage.server.console</artifactId>

View file

@ -60,7 +60,7 @@ public class ConsoleFrame extends javax.swing.JFrame implements MageClient {
initComponents();
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
session = new SessionImpl(this);
connectDialog = new ConnectDialog();
} catch (Exception ex) {

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-deck-constructed</artifactId>

View file

@ -25,9 +25,11 @@ public class Brawl extends Constructed {
setCodes.addAll(Standard.makeLegalSets());
banned.add("Golos, Tireless Pilgrim");
banned.add("Drannith Magistrate");
banned.add("Lutri, the Spellchaser");
banned.add("Oko, Thief of Crowns");
banned.add("Sorcerous Spyglass");
banned.add("Winota, Joiner of Forces");
}
public Brawl(String name) {

View file

@ -13,24 +13,31 @@ public class DuelCommander extends Commander {
banned.add("Ancient Tomb");
banned.add("Back to Basics");
banned.add("Black Lotus");
banned.add("Capture of Jingzhou");
banned.add("Cavern of Souls");
banned.add("Channel");
banned.add("Chrome Mox");
banned.add("Deflecting Swat");
banned.add("Dig Through Time");
banned.add("Eidolon of the Great Revel");
banned.add("Emrakul, the Aeons Torn");
banned.add("Entomb");
banned.add("Fastbond");
banned.add("Field of the Dead");
banned.add("Fierce Guardianship");
banned.add("Fireblast");
banned.add("Food Chain");
banned.add("Gaea's Cradle");
banned.add("Gifts Ungiven");
banned.add("Grim Monolith");
banned.add("Hermit Druid");
banned.add("High Tide");
banned.add("Humility");
banned.add("Imperial Seal");
banned.add("Karakas");
banned.add("Library of Alexandria");
banned.add("Lion's Eye Diamond");
banned.add("Loyal Retainers");
banned.add("Lutri, the Spellchaser");
banned.add("Mana Crypt");
banned.add("Mana Drain");
banned.add("Mana Vault");
@ -53,15 +60,18 @@ public class DuelCommander extends Commander {
banned.add("Sensei's Divining Top");
banned.add("Sol Ring");
banned.add("Strip Mine");
banned.add("Temporal Manipulation");
banned.add("Thassa's Oracle");
banned.add("The Tabernacle at Pendrell Vale");
banned.add("Time Vault");
banned.add("Time Walk");
banned.add("Timetwister");
banned.add("Time Warp");
banned.add("Tinker");
banned.add("Tolarian Academy");
banned.add("Treasure Cruise");
banned.add("Vampiric Tutor");
banned.add("Wasteland");
bannedCommander.add("Arahbo, Roar of the World");
bannedCommander.add("Baral, Chief of Compliance");
@ -72,7 +82,6 @@ public class DuelCommander extends Commander {
bannedCommander.add("Edric, Spymaster of Trest");
bannedCommander.add("Emry, Lurker of the Loch");
bannedCommander.add("Geist of Saint Traft");
bannedCommander.add("Jace, Vryn's Prodigy");
bannedCommander.add("Marath, Will of the Wild");
bannedCommander.add("Najeela, the Blade-Blossom");
bannedCommander.add("Oloro, Ageless Ascetic");

View file

@ -49,6 +49,7 @@ public class Legacy extends Constructed {
banned.add("Iterative Analysis");
banned.add("Jeweled Bird");
banned.add("Library of Alexandria");
banned.add("Lurrus of the Dream-Den");
banned.add("Mana Crypt");
banned.add("Mana Drain");
banned.add("Mana Vault");
@ -94,6 +95,6 @@ public class Legacy extends Constructed {
banned.add("Wrenn and Six");
banned.add("Yawgmoth's Bargain");
banned.add("Yawgmoth's Will");
banned.add("Zirda, the Dawnwaker");
}
}

View file

@ -17,7 +17,9 @@ public class Standard extends Constructed {
setCodes.addAll(makeLegalSets());
banned.add("Agent of Treachery");
banned.add("Field of the Dead");
banned.add("Fires of Invention");
banned.add("Oko, Thief of Crowns");
banned.add("Once Upon a Time");
banned.add("Veil of Summer");

View file

@ -30,6 +30,7 @@ public class Vintage extends Constructed {
banned.add("Immediate Action");
banned.add("Iterative Analysis");
banned.add("Jeweled Bird");
banned.add("Lurrus of the Dream-Den");
banned.add("Muzzio's Preparations");
banned.add("Power Play");
banned.add("Rebirth");

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-deck-limited</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-brawlduel</artifactId>

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-brawlfreeforall</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-canadianhighlanderduel</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-commanderduel</artifactId>

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-commanderfreeforall</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-freeforall</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-freeformcommanderduel</artifactId>

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-freeformcommanderfreeforall</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-freeformunlimitedcommander</artifactId>
@ -23,7 +23,7 @@
<dependency>
<groupId>org.mage</groupId>
<artifactId>mage-game-freeformcommanderfreeforall</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
<scope>compile</scope>
</dependency>
</dependencies>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-momirduel</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-momirfreeforall</artifactId>

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-oathbreakerduel</artifactId>
@ -22,7 +22,7 @@
<dependency>
<groupId>org.mage</groupId>
<artifactId>mage-game-oathbreakerfreeforall</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
<scope>compile</scope>
</dependency>
</dependencies>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-oathbreakerfreeforall</artifactId>

View file

@ -25,8 +25,8 @@ import java.util.*;
public class OathbreakerFreeForAll extends GameCommanderImpl {
private int numPlayers;
private Map<UUID, Set<UUID>> playerSignatureSpells = new HashMap<>();
private Map<UUID, Set<UUID>> playerOathbreakers = new HashMap<>();
private final Map<UUID, Set<UUID>> playerSignatureSpells = new HashMap<>();
private final Map<UUID, Set<UUID>> playerOathbreakers = new HashMap<>();
private static final String COMMANDER_NAME_OATHBREAKER = "Oathbreaker";
private static final String COMMANDER_NAME_SIGNATURE_SPELL = "Signature Spell";
@ -122,19 +122,19 @@ public class OathbreakerFreeForAll extends GameCommanderImpl {
if (player != null) {
Set<UUID> commanders = this.playerOathbreakers.getOrDefault(player.getId(), new HashSet<>());
Set<UUID> spells = this.playerSignatureSpells.getOrDefault(player.getId(), new HashSet<>());
for (UUID id : player.getCommandersIds()) {
for (UUID commanderId : super.getCommandersIds(player, commanderCardType)) {
switch (commanderCardType) {
case ANY:
res.add(id);
res.add(commanderId);
break;
case COMMANDER_OR_OATHBREAKER:
if (commanders.contains(id)) {
res.add(id);
if (commanders.contains(commanderId)) {
res.add(commanderId);
}
break;
case SIGNATURE_SPELL:
if (spells.contains(id)) {
res.add(id);
if (spells.contains(commanderId)) {
res.add(commanderId);
}
break;
default:

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-pennydreadfulcommanderfreeforall</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-tinyleadersduel</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-game-twoplayerduel</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-player-ai-draftbot</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-player-ai-ma</artifactId>

View file

@ -3,6 +3,7 @@ package mage.player.ai;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.AbilityImpl;
import mage.abilities.ActivatedAbility;
import mage.abilities.TriggeredAbility;
import mage.abilities.common.PassAbility;
import mage.abilities.costs.mana.ManaCost;
@ -94,9 +95,9 @@ public class SimulatedPlayer2 extends ComputerPlayer {
}
protected void simulateOptions(Game game) {
List<Ability> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
List<ActivatedAbility> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
playables = filterAbilities(game, playables, suggested);
for (Ability ability : playables) {
for (ActivatedAbility ability : playables) {
if (ability.getAbilityType() == AbilityType.MANA) {
continue;
}
@ -186,15 +187,15 @@ public class SimulatedPlayer2 extends ComputerPlayer {
* @param suggested
* @return
*/
protected List<Ability> filterAbilities(Game game, List<Ability> playables, List<String> suggested) {
protected List<ActivatedAbility> filterAbilities(Game game, List<ActivatedAbility> playables, List<String> suggested) {
if (playables.isEmpty()) {
return playables;
}
if (suggested == null || suggested.isEmpty()) {
return playables;
}
List<Ability> filtered = new ArrayList<>();
for (Ability ability : playables) {
List<ActivatedAbility> filtered = new ArrayList<>();
for (ActivatedAbility ability : playables) {
Card card = game.getCard(ability.getSourceId());
if (card != null) {
for (String s : suggested) {
@ -212,7 +213,7 @@ public class SimulatedPlayer2 extends ComputerPlayer {
return playables;
}
protected List<Ability> filterOptions(Game game, List<Ability> options, Ability ability, List<String> suggested) {
protected List<Ability> filterOptions(Game game, List<Ability> options, ActivatedAbility ability, List<String> suggested) {
if (options.isEmpty()) {
return options;
}

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-player-ai</artifactId>

View file

@ -396,12 +396,16 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
}
while ((outcome.isGood() ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen())
// exile cost workaround: exile is bad, but exile from graveyard in most cases is good (more exiled -- more good things you get, e.g. delve's pay)
boolean isRealGood = outcome.isGood() || outcome == Outcome.Exile;
while ((isRealGood ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen())
&& !cards.isEmpty()) {
Card pick = pickTarget(abilityControllerId, cards, outcome, target, null, game);
if (pick != null) {
target.addTarget(pick.getId(), null, game);
cards.remove(pick);
} else {
break;
}
}
@ -1267,7 +1271,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
//play a land that will allow us to play an unplayable
for (Mana mana : unplayable.keySet()) {
for (Card card : lands) {
for (ActivatedManaAbilityImpl ability : card.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD)) {
for (ActivatedManaAbilityImpl ability : card.getAbilities(game).getActivatedManaAbilities(Zone.BATTLEFIELD)) {
for (Mana netMana : ability.getNetMana(game)) {
if (netMana.enough(mana)) {
this.playLand(card, game, false);
@ -1281,7 +1285,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
//play a land that will get us closer to playing an unplayable
for (Mana mana : unplayable.keySet()) {
for (Card card : lands) {
for (ActivatedManaAbilityImpl ability : card.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD)) {
for (ActivatedManaAbilityImpl ability : card.getAbilities(game).getActivatedManaAbilities(Zone.BATTLEFIELD)) {
for (Mana netMana : ability.getNetMana(game)) {
if (mana.contains(netMana)) {
this.playLand(card, game, false);
@ -1321,7 +1325,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
if (ability != null && ability.canActivate(playerId, game).canActivate()
&& !game.getContinuousEffects().preventedByRuleModification(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getSourceId(), ability.getSourceId(), playerId), ability, game, true)) {
if (card.getCardType().contains(CardType.INSTANT)
|| card.hasAbility(FlashAbility.getInstance().getId(), game)) {
|| card.hasAbility(FlashAbility.getInstance(), game)) {
playableInstant.add(card);
} else {
playableNonInstant.add(card);
@ -1362,7 +1366,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
}
for (Card card : graveyard.getCards(game)) {
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.GRAVEYARD)) {
for (ActivatedAbility ability : card.getAbilities(game).getActivatedAbilities(Zone.GRAVEYARD)) {
if (ability.canActivate(playerId, game).canActivate()) {
ManaOptions abilityOptions = ability.getManaCosts().getOptions();
if (abilityOptions.isEmpty()) {
@ -1529,10 +1533,34 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
}
}
// pay phyrexian life costs
if (cost instanceof PhyrexianManaCost) {
return cost.pay(null, game, null, playerId, false, null) || permittingObject != null;
}
// pay special mana like convoke cost (tap for pay)
// GUI: user see "special" button while pay spell's cost
// TODO: AI can't prioritize special mana types to pay, e.g. it will use first available
SpecialAction specialAction = game.getState().getSpecialActions().getControlledBy(this.getId(), true)
.values().stream().findFirst().orElse(null);
ManaOptions specialMana = specialAction == null ? null : specialAction.getManaOptions(ability, game, unpaid);
if (specialMana != null) {
for (Mana netMana : specialMana) {
if (cost.testPay(netMana) || permittingObject != null) {
if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) {
continue;
}
specialAction.setUnpaidMana(unpaid);
if (activateAbility(specialAction, game)) {
return true;
}
// only one time try to pay
break;
}
}
}
return false;
}
@ -1895,27 +1923,6 @@ public class ComputerPlayer extends PlayerImpl implements Player {
return 0;
}
@Override
public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
switch (ability.getSpellAbilityType()) {
case SPLIT:
case SPLIT_FUSED:
case SPLIT_AFTERMATH:
MageObject object = game.getObject(ability.getSourceId());
if (object != null) {
LinkedHashMap<UUID, ActivatedAbility> useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game);
if (useableAbilities != null && !useableAbilities.isEmpty()) {
// game.fireGetChoiceEvent(playerId, name, object, new ArrayList<>(useableAbilities.values()));
// TODO: Improve this
return (SpellAbility) useableAbilities.values().iterator().next();
}
}
return null;
default:
return ability;
}
}
@Override
public Mode chooseMode(Modes modes, Ability source, Game game) {
log.debug("chooseMode");

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-player-ai-mcts</artifactId>

View file

@ -2,6 +2,7 @@
package mage.player.ai;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.SpellAbility;
import mage.abilities.common.PassAbility;
import mage.abilities.costs.mana.GenericManaCost;
@ -45,16 +46,16 @@ public class MCTSPlayer extends ComputerPlayer {
return new MCTSPlayer(this);
}
protected List<Ability> getPlayableAbilities(Game game) {
List<Ability> playables = getPlayable(game, true);
protected List<ActivatedAbility> getPlayableAbilities(Game game) {
List<ActivatedAbility> playables = getPlayable(game, true);
playables.add(pass);
return playables;
}
public List<Ability> getPlayableOptions(Game game) {
List<Ability> all = new ArrayList<>();
List<Ability> playables = getPlayableAbilities(game);
for (Ability ability: playables) {
List<ActivatedAbility> playables = getPlayableAbilities(game);
for (ActivatedAbility ability: playables) {
List<Ability> options = game.getPlayer(playerId).getPlayableOptions(ability, game);
if (options.isEmpty()) {
if (!ability.getManaCosts().getVariableCosts().isEmpty()) {

View file

@ -73,7 +73,7 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
}
private Ability getAction(Game game) {
List<Ability> playables = getPlayableAbilities(game);
List<ActivatedAbility> playables = getPlayableAbilities(game);
Ability ability;
while (true) {
if (playables.size() == 1) {

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-player-aiminimax</artifactId>

View file

@ -3,6 +3,7 @@
package mage.player.ai;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.SpellAbility;
import mage.abilities.TriggeredAbility;
import mage.abilities.common.PassAbility;
@ -59,10 +60,10 @@ public class SimulatedPlayer extends ComputerPlayer {
return list;
}
protected void simulateOptions(Game game, Ability previousActions) {
protected void simulateOptions(Game game, ActivatedAbility previousActions) {
allActions.add(previousActions);
List<Ability> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
for (Ability ability: playables) {
List<ActivatedAbility> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
for (ActivatedAbility ability: playables) {
List<Ability> options = game.getPlayer(playerId).getPlayableOptions(ability, game);
if (options.isEmpty()) {
if (!ability.getManaCosts().getVariableCosts().isEmpty()) {

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-player-human</artifactId>

View file

@ -10,6 +10,7 @@ import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.RequirementEffect;
import mage.abilities.hint.HintUtils;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.abilities.mana.ManaAbility;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.decks.Deck;
@ -61,6 +62,8 @@ import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
*/
public class HumanPlayer extends PlayerImpl {
private static final boolean ALLOW_USERS_TO_PUT_NON_PLAYABLE_SPELLS_ON_STACK_WORKAROUND = false; // warning, see workaround's info on usage
private transient Boolean responseOpenedForAnswer = false; // can't get response until prepared target (e.g. until send all fire events to all players)
private final transient PlayerResponse response = new PlayerResponse();
@ -1039,7 +1042,7 @@ public class HumanPlayer extends PlayerImpl {
if (response.getString() != null
&& response.getString().equals("special")) {
specialAction(game);
activateSpecialAction(game, null);
} else if (response.getUUID() != null) {
boolean result = false;
MageObject object = game.getObject(response.getUUID());
@ -1047,24 +1050,42 @@ public class HumanPlayer extends PlayerImpl {
Zone zone = game.getState().getZone(object.getId());
if (zone != null) {
// look at card or try to cast/activate abilities
LinkedHashMap<UUID, ActivatedAbility> useableAbilities = new LinkedHashMap<>();
Player actingPlayer = null;
LinkedHashMap<UUID, ActivatedAbility> useableAbilities = null;
if (playerId.equals(game.getPriorityPlayerId())) {
actingPlayer = this;
} else if (getPlayersUnderYourControl().contains(game.getPriorityPlayerId())) {
actingPlayer = game.getPlayer(game.getPriorityPlayerId());
}
if (actingPlayer != null) {
useableAbilities = actingPlayer.getUseableActivatedAbilities(object, zone, game);
useableAbilities = actingPlayer.getPlayableActivatedAbilities(object, zone, game);
// GUI: workaround to enable users to put spells on stack without real available mana
// (without highlighting, like it was in old versions before June 2020)
// Reason: some gain ability adds cost modification and other things to spells on stack only,
// e.g. xmage can't find playable ability before put that spell on stack (wtf example: Chief Engineer,
// see ConvokeTest)
// TODO: it's a BAD workaround -- users can't see that card/ability is broken and will not report to us, AI can't play that ability too
// Enable it on massive broken cards/abilities only or for manual tests
if (ALLOW_USERS_TO_PUT_NON_PLAYABLE_SPELLS_ON_STACK_WORKAROUND) {
if (object instanceof Card) {
for (Ability ability : ((Card) object).getAbilities(game)) {
if (ability instanceof SpellAbility && ((SpellAbility) ability).canActivate(actingPlayer.getId(), game).canActivate()
|| ability instanceof PlayLandAbility) {
useableAbilities.putIfAbsent(ability.getId(), (ActivatedAbility) ability);
}
}
}
}
}
if (object instanceof Card
&& ((Card) object).isFaceDown(game)
&& lookAtFaceDownCard((Card) object, game, useableAbilities == null ? 0 : useableAbilities.size())) {
&& lookAtFaceDownCard((Card) object, game, useableAbilities.size())) {
result = true;
} else {
if (useableAbilities != null
&& !useableAbilities.isEmpty()) {
if (!useableAbilities.isEmpty()) {
activateAbility(useableAbilities, object, game);
result = true;
}
@ -1221,7 +1242,7 @@ public class HumanPlayer extends PlayerImpl {
} else if (response.getString() != null
&& response.getString().equals("special")) {
if (unpaid instanceof ManaCostsImpl) {
specialManaAction(unpaid, game);
activateSpecialAction(game, unpaid);
}
} else if (response.getManaType() != null) {
// this mana type can be paid once from pool
@ -1336,19 +1357,30 @@ public class HumanPlayer extends PlayerImpl {
if (object == null) {
return;
}
if (AbilityType.SPELL.equals(abilityToCast.getAbilityType())) {
// GUI: for user's information only - check if mana abilities allows to use here (getUseableManaAbilities already filter it)
// Reason: when you use special mana ability then normal mana abilities will be restricted to pay. Users
// can't see lands as playable and must know the reason (if they click on land then they get that message)
if (abilityToCast.getAbilityType() == AbilityType.SPELL) {
Spell spell = game.getStack().getSpell(abilityToCast.getSourceId());
if (spell != null && !spell.isResolving()
&& spell.isDoneActivatingManaAbilities()) {
game.informPlayer(this, "You can no longer use activated mana abilities to pay for the current spell. Cancel and recast the spell and activate mana abilities first.");
return;
boolean haveManaAbilities = object.getAbilities().stream().anyMatch(a -> a instanceof ManaAbility);
if (spell != null && !spell.isResolving() && haveManaAbilities) {
switch (spell.getCurrentActivatingManaAbilitiesStep()) {
// if you used special mana ability like convoke then normal mana abilities will be restricted to use, see Convoke for details
case BEFORE:
case NORMAL:
break;
case AFTER:
game.informPlayer(this, "You can no longer use activated mana abilities to pay for the current spell (special mana pay already used). Cancel and recast the spell to activate mana abilities first.");
return;
}
}
}
Zone zone = game.getState().getZone(object.getId());
if (zone != null) {
LinkedHashMap<UUID, ActivatedManaAbilityImpl> useableAbilities = getUseableManaAbilities(object, zone, game);
if (useableAbilities != null
&& !useableAbilities.isEmpty()) {
if (!useableAbilities.isEmpty()) {
useableAbilities = ManaUtil.tryToAutoPay(unpaid, useableAbilities); // eliminates other abilities if one fits perfectly
currentlyUnpaidMana = unpaid;
activateAbility(useableAbilities, object, game);
@ -1845,7 +1877,13 @@ public class HumanPlayer extends PlayerImpl {
draft.firePickCardEvent(playerId);
}
protected void specialAction(Game game) {
/**
* Activate special action (normal or mana)
*
* @param game
* @param unpaidForManaAction - set unpaid for mana actions like convoke
*/
protected void activateSpecialAction(Game game, ManaCost unpaidForManaAction) {
if (gameInCheckPlayableState(game)) {
return;
}
@ -1854,7 +1892,7 @@ public class HumanPlayer extends PlayerImpl {
return;
}
Map<UUID, SpecialAction> specialActions = game.getState().getSpecialActions().getControlledBy(playerId, false);
Map<UUID, SpecialAction> specialActions = game.getState().getSpecialActions().getControlledBy(playerId, unpaidForManaAction != null);
if (!specialActions.isEmpty()) {
updateGameStatePriority("specialAction", game);
@ -1864,39 +1902,13 @@ public class HumanPlayer extends PlayerImpl {
}
waitForResponse(game);
if (response.getUUID() != null) {
if (specialActions.containsKey(response.getUUID())) {
activateAbility(specialActions.get(response.getUUID()), game);
}
}
}
}
protected void specialManaAction(ManaCost unpaid, Game game) {
if (gameInCheckPlayableState(game)) {
return;
}
if (!canRespond()) {
return;
}
Map<UUID, SpecialAction> specialActions = game.getState().getSpecialActions().getControlledBy(playerId, true);
if (!specialActions.isEmpty()) {
updateGameStatePriority("specialAction", game);
prepareForResponse(game);
if (!isExecutingMacro()) {
game.fireGetChoiceEvent(playerId, name, null, new ArrayList<>(specialActions.values()));
}
waitForResponse(game);
if (response.getUUID() != null) {
if (specialActions.containsKey(response.getUUID())) {
SpecialAction specialAction = specialActions.get(response.getUUID());
if (specialAction != null) {
specialAction.setUnpaidMana(unpaid);
activateAbility(specialActions.get(response.getUUID()), game);
if (unpaidForManaAction != null) {
specialAction.setUnpaidMana(unpaidForManaAction);
}
activateAbility(specialAction, game);
}
}
}
@ -1973,51 +1985,6 @@ public class HumanPlayer extends PlayerImpl {
return true;
}
@Override
public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) {
if (gameInCheckPlayableState(game)) {
return null;
}
// TODO: add canRespond cycle?
if (!canRespond()) {
return null;
}
switch (ability.getSpellAbilityType()) {
case SPLIT:
case SPLIT_FUSED:
case SPLIT_AFTERMATH:
MageObject object = game.getObject(ability.getSourceId());
if (object != null) {
String message = "Choose ability to cast" + (noMana ? " for FREE" : "") + "<br>" + object.getLogName();
LinkedHashMap<UUID, ActivatedAbility> useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game);
if (useableAbilities != null
&& useableAbilities.size() == 1) {
return (SpellAbility) useableAbilities.values().iterator().next();
} else if (useableAbilities != null
&& !useableAbilities.isEmpty()) {
updateGameStatePriority("chooseSpellAbilityForCast", game);
prepareForResponse(game);
if (!isExecutingMacro()) {
game.fireGetChoiceEvent(playerId, message, object, new ArrayList<>(useableAbilities.values()));
}
waitForResponse(game);
if (response.getUUID() != null) {
if (useableAbilities.containsKey(response.getUUID())) {
return (SpellAbility) useableAbilities.get(response.getUUID());
}
}
}
}
return null;
default:
return ability;
}
}
@Override
public SpellAbility chooseAbilityForCast(Card card, Game game, boolean nonMana) {
if (gameInCheckPlayableState(game)) {
@ -2096,6 +2063,9 @@ public class HumanPlayer extends PlayerImpl {
modeText = "(selected " + timesSelected + "x) " + modeText;
}
}
if (!modeText.isEmpty()) {
modeText = Character.toUpperCase(modeText.charAt(0)) + modeText.substring(1);
}
modeMap.put(mode.getId(), modeIndex + ". " + modeText);
}
}
@ -2141,13 +2111,14 @@ public class HumanPlayer extends PlayerImpl {
// cancel choice (remove all selections)
if (Modes.CHOOSE_OPTION_CANCEL_ID.equals(response.getUUID())) {
modes.getSelectedModes().clear();
return null;
}
} else if (canEndChoice) {
// end choice by done button in feedback panel
// disable after done option implemented
// done = true;
}
// triggered abilities can't be skipped by cancel or wrong answer
if (source.getAbilityType() != AbilityType.TRIGGERED) {
done = true;
}

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-tournament-boosterdraft</artifactId>

View file

@ -0,0 +1,552 @@
package mage.tournament.cubes;
import mage.game.draft.DraftCube;
public class VintageCubeApril2020 extends DraftCube {
public VintageCubeApril2020() {
super("MTGO Vintage Cube April 2020");
cubeCards.add(new CardIdentity("Kytheon, Hero of Akros", ""));
cubeCards.add(new CardIdentity("Mother of Runes", ""));
cubeCards.add(new CardIdentity("Student of Warfare", ""));
cubeCards.add(new CardIdentity("Adanto Vanguard", ""));
cubeCards.add(new CardIdentity("Containment Priest", ""));
cubeCards.add(new CardIdentity("Leonin Relic-Warder", ""));
cubeCards.add(new CardIdentity("Porcelain Legionnaire", ""));
cubeCards.add(new CardIdentity("Selfless Spirit", ""));
cubeCards.add(new CardIdentity("Soulfire Grand Master", ""));
cubeCards.add(new CardIdentity("Stoneforge Mystic", ""));
cubeCards.add(new CardIdentity("Thalia, Guardian of Thraben", ""));
cubeCards.add(new CardIdentity("Tithe Taker", ""));
cubeCards.add(new CardIdentity("Wall of Omens", ""));
cubeCards.add(new CardIdentity("Blade Splicer", ""));
cubeCards.add(new CardIdentity("Brightling", ""));
cubeCards.add(new CardIdentity("Brimaz, King of Oreskos", ""));
cubeCards.add(new CardIdentity("Fairgrounds Warden", ""));
cubeCards.add(new CardIdentity("Flickerwisp", ""));
cubeCards.add(new CardIdentity("Monastery Mentor", ""));
cubeCards.add(new CardIdentity("Recruiter of the Guard", ""));
cubeCards.add(new CardIdentity("Silverblade Paladin", ""));
cubeCards.add(new CardIdentity("Emeria Angel", ""));
cubeCards.add(new CardIdentity("Hero of Bladehold", ""));
cubeCards.add(new CardIdentity("Linvala, Keeper of Silence", ""));
cubeCards.add(new CardIdentity("Restoration Angel", ""));
cubeCards.add(new CardIdentity("Angel of Invention", ""));
cubeCards.add(new CardIdentity("Elspeth Conquers Death", ""));
cubeCards.add(new CardIdentity("Archangel Avacyn", ""));
cubeCards.add(new CardIdentity("Baneslayer Angel", ""));
cubeCards.add(new CardIdentity("Lyra Dawnbringer", ""));
cubeCards.add(new CardIdentity("Reveillark", ""));
cubeCards.add(new CardIdentity("Sun Titan", ""));
cubeCards.add(new CardIdentity("Angel of Serenity", ""));
cubeCards.add(new CardIdentity("Elesh Norn, Grand Cenobite", ""));
cubeCards.add(new CardIdentity("Iona, Shield of Emeria", ""));
cubeCards.add(new CardIdentity("Gideon Blackblade", ""));
cubeCards.add(new CardIdentity("Elspeth, Sun's Nemesis", ""));
cubeCards.add(new CardIdentity("Gideon, Ally of Zendikar", ""));
cubeCards.add(new CardIdentity("Gideon Jura", ""));
cubeCards.add(new CardIdentity("Elspeth, Sun's Champion", ""));
cubeCards.add(new CardIdentity("Condemn", ""));
cubeCards.add(new CardIdentity("Enlightened Tutor", ""));
cubeCards.add(new CardIdentity("Mana Tithe", ""));
cubeCards.add(new CardIdentity("Path to Exile", ""));
cubeCards.add(new CardIdentity("Swords to Plowshares", ""));
cubeCards.add(new CardIdentity("Disenchant", ""));
cubeCards.add(new CardIdentity("Unexpectedly Absent", ""));
cubeCards.add(new CardIdentity("Balance", ""));
cubeCards.add(new CardIdentity("Council's Judgment", ""));
cubeCards.add(new CardIdentity("Spectral Procession", ""));
cubeCards.add(new CardIdentity("Armageddon", ""));
cubeCards.add(new CardIdentity("Day of Judgment", ""));
cubeCards.add(new CardIdentity("Ravages of War", ""));
cubeCards.add(new CardIdentity("Wrath of God", ""));
cubeCards.add(new CardIdentity("Terminus", ""));
cubeCards.add(new CardIdentity("Land Tax", ""));
cubeCards.add(new CardIdentity("Legion's Landing", ""));
cubeCards.add(new CardIdentity("Honor of the Pure", ""));
cubeCards.add(new CardIdentity("Banishing Light", ""));
cubeCards.add(new CardIdentity("Oblivion Ring", ""));
cubeCards.add(new CardIdentity("Faith's Fetters", ""));
cubeCards.add(new CardIdentity("Moat", ""));
cubeCards.add(new CardIdentity("Parallax Wave", ""));
cubeCards.add(new CardIdentity("Spear of Heliod", ""));
cubeCards.add(new CardIdentity("Karakas", ""));
cubeCards.add(new CardIdentity("Thassa's Oracle", ""));
cubeCards.add(new CardIdentity("Baral, Chief of Compliance", ""));
cubeCards.add(new CardIdentity("Jace, Vryn's Prodigy", ""));
cubeCards.add(new CardIdentity("Looter il-Kor", ""));
cubeCards.add(new CardIdentity("Phantasmal Image", ""));
cubeCards.add(new CardIdentity("Snapcaster Mage", ""));
cubeCards.add(new CardIdentity("Thing in the Ice", ""));
cubeCards.add(new CardIdentity("Arcane Artisan", ""));
cubeCards.add(new CardIdentity("Deceiver Exarch", ""));
cubeCards.add(new CardIdentity("Pestermite", ""));
cubeCards.add(new CardIdentity("Spellseeker", ""));
cubeCards.add(new CardIdentity("Trinket Mage", ""));
cubeCards.add(new CardIdentity("Vendilion Clique", ""));
cubeCards.add(new CardIdentity("Glen Elendra Archmage", ""));
cubeCards.add(new CardIdentity("Phyrexian Metamorph", ""));
cubeCards.add(new CardIdentity("Sower of Temptation", ""));
cubeCards.add(new CardIdentity("Venser, Shaper Savant", ""));
cubeCards.add(new CardIdentity("Mulldrifter", ""));
cubeCards.add(new CardIdentity("Riftwing Cloudskate", ""));
cubeCards.add(new CardIdentity("Consecrated Sphinx", ""));
cubeCards.add(new CardIdentity("Frost Titan", ""));
cubeCards.add(new CardIdentity("Torrential Gearhulk", ""));
cubeCards.add(new CardIdentity("Palinchron", ""));
cubeCards.add(new CardIdentity("Inkwell Leviathan", ""));
cubeCards.add(new CardIdentity("Jace Beleren", ""));
cubeCards.add(new CardIdentity("Jace, the Mind Sculptor", ""));
cubeCards.add(new CardIdentity("Tezzeret the Seeker", ""));
cubeCards.add(new CardIdentity("Ancestral Recall", ""));
cubeCards.add(new CardIdentity("Brainstorm", ""));
cubeCards.add(new CardIdentity("High Tide", ""));
cubeCards.add(new CardIdentity("Mystical Tutor", ""));
cubeCards.add(new CardIdentity("Spell Pierce", ""));
cubeCards.add(new CardIdentity("Brain Freeze", ""));
cubeCards.add(new CardIdentity("Counterspell", ""));
cubeCards.add(new CardIdentity("Daze", ""));
cubeCards.add(new CardIdentity("Impulse", ""));
cubeCards.add(new CardIdentity("Mana Drain", ""));
cubeCards.add(new CardIdentity("Mana Leak", ""));
cubeCards.add(new CardIdentity("Miscalculation", ""));
cubeCards.add(new CardIdentity("Remand", ""));
cubeCards.add(new CardIdentity("Frantic Search", ""));
cubeCards.add(new CardIdentity("Thirst for Knowledge", ""));
cubeCards.add(new CardIdentity("Cryptic Command", ""));
cubeCards.add(new CardIdentity("Fact or Fiction", ""));
cubeCards.add(new CardIdentity("Gifts Ungiven", ""));
cubeCards.add(new CardIdentity("Turnabout", ""));
cubeCards.add(new CardIdentity("Force of Will", ""));
cubeCards.add(new CardIdentity("Gush", ""));
cubeCards.add(new CardIdentity("Mystic Confluence", ""));
cubeCards.add(new CardIdentity("Repeal", ""));
cubeCards.add(new CardIdentity("Dig Through Time", ""));
cubeCards.add(new CardIdentity("Ancestral Vision", ""));
cubeCards.add(new CardIdentity("Gitaxian Probe", ""));
cubeCards.add(new CardIdentity("Ponder", ""));
cubeCards.add(new CardIdentity("Preordain", ""));
cubeCards.add(new CardIdentity("Chart a Course", ""));
cubeCards.add(new CardIdentity("Time Walk", ""));
cubeCards.add(new CardIdentity("Show and Tell", ""));
cubeCards.add(new CardIdentity("Timetwister", ""));
cubeCards.add(new CardIdentity("Tinker", ""));
cubeCards.add(new CardIdentity("Bribery", ""));
cubeCards.add(new CardIdentity("Time Warp", ""));
cubeCards.add(new CardIdentity("Mind's Desire", ""));
cubeCards.add(new CardIdentity("Time Spiral", ""));
cubeCards.add(new CardIdentity("Upheaval", ""));
cubeCards.add(new CardIdentity("Treasure Cruise", ""));
cubeCards.add(new CardIdentity("Search for Azcanta", ""));
cubeCards.add(new CardIdentity("Control Magic", ""));
cubeCards.add(new CardIdentity("Opposition", ""));
cubeCards.add(new CardIdentity("Treachery", ""));
cubeCards.add(new CardIdentity("Shelldock Isle", ""));
cubeCards.add(new CardIdentity("Tolarian Academy", ""));
cubeCards.add(new CardIdentity("Putrid Imp", ""));
cubeCards.add(new CardIdentity("Dark Confidant", ""));
cubeCards.add(new CardIdentity("Kitesail Freebooter", ""));
cubeCards.add(new CardIdentity("Mesmeric Fiend", ""));
cubeCards.add(new CardIdentity("Oona's Prowler", ""));
cubeCards.add(new CardIdentity("Pack Rat", ""));
cubeCards.add(new CardIdentity("Vampire Hexmage", ""));
cubeCards.add(new CardIdentity("Bone Shredder", ""));
cubeCards.add(new CardIdentity("Hypnotic Specter", ""));
cubeCards.add(new CardIdentity("Ophiomancer", ""));
cubeCards.add(new CardIdentity("Plaguecrafter", ""));
cubeCards.add(new CardIdentity("Vampire Nighthawk", ""));
cubeCards.add(new CardIdentity("Gonti, Lord of Luxury", ""));
cubeCards.add(new CardIdentity("Nekrataal", ""));
cubeCards.add(new CardIdentity("Ravenous Chupacabra", ""));
cubeCards.add(new CardIdentity("Shriekmaw", ""));
cubeCards.add(new CardIdentity("Grave Titan", ""));
cubeCards.add(new CardIdentity("Ink-Eyes, Servant of Oni", ""));
cubeCards.add(new CardIdentity("Massacre Wurm", ""));
cubeCards.add(new CardIdentity("Tasigur, the Golden Fang", ""));
cubeCards.add(new CardIdentity("Sheoldred, Whispering One", ""));
cubeCards.add(new CardIdentity("Griselbrand", ""));
cubeCards.add(new CardIdentity("Liliana of the Veil", ""));
cubeCards.add(new CardIdentity("Liliana, Death's Majesty", ""));
cubeCards.add(new CardIdentity("Dark Ritual", ""));
cubeCards.add(new CardIdentity("Entomb", ""));
cubeCards.add(new CardIdentity("Fatal Push", ""));
cubeCards.add(new CardIdentity("Vampiric Tutor", ""));
cubeCards.add(new CardIdentity("Cabal Ritual", ""));
cubeCards.add(new CardIdentity("Go for the Throat", ""));
cubeCards.add(new CardIdentity("Liliana's Triumph", ""));
cubeCards.add(new CardIdentity("Shallow Grave", ""));
cubeCards.add(new CardIdentity("Ultimate Price", ""));
cubeCards.add(new CardIdentity("Corpse Dance", ""));
cubeCards.add(new CardIdentity("Dismember", ""));
cubeCards.add(new CardIdentity("Hero's Downfall", ""));
cubeCards.add(new CardIdentity("Makeshift Mannequin", ""));
cubeCards.add(new CardIdentity("Duress", ""));
cubeCards.add(new CardIdentity("Imperial Seal", ""));
cubeCards.add(new CardIdentity("Inquisition of Kozilek", ""));
cubeCards.add(new CardIdentity("Reanimate", ""));
cubeCards.add(new CardIdentity("Thoughtseize", ""));
cubeCards.add(new CardIdentity("Collective Brutality", ""));
cubeCards.add(new CardIdentity("Demonic Tutor", ""));
cubeCards.add(new CardIdentity("Exhume", ""));
cubeCards.add(new CardIdentity("Hymn to Tourach", ""));
cubeCards.add(new CardIdentity("Night's Whisper", ""));
cubeCards.add(new CardIdentity("Buried Alive", ""));
cubeCards.add(new CardIdentity("Toxic Deluge", ""));
cubeCards.add(new CardIdentity("Yawgmoth's Will", ""));
cubeCards.add(new CardIdentity("Damnation", ""));
cubeCards.add(new CardIdentity("Languish", ""));
cubeCards.add(new CardIdentity("Mastermind's Acquisition", ""));
cubeCards.add(new CardIdentity("Tendrils of Agony", ""));
cubeCards.add(new CardIdentity("Dark Petition", ""));
cubeCards.add(new CardIdentity("Living Death", ""));
cubeCards.add(new CardIdentity("Mind Twist", ""));
cubeCards.add(new CardIdentity("Animate Dead", ""));
cubeCards.add(new CardIdentity("Bitterblossom", ""));
cubeCards.add(new CardIdentity("Necromancy", ""));
cubeCards.add(new CardIdentity("Phyrexian Arena", ""));
cubeCards.add(new CardIdentity("Recurring Nightmare", ""));
cubeCards.add(new CardIdentity("Yawgmoth's Bargain", ""));
cubeCards.add(new CardIdentity("Goblin Guide", ""));
cubeCards.add(new CardIdentity("Goblin Welder", ""));
cubeCards.add(new CardIdentity("Grim Lavamancer", ""));
cubeCards.add(new CardIdentity("Jackal Pup", ""));
cubeCards.add(new CardIdentity("Monastery Swiftspear", ""));
cubeCards.add(new CardIdentity("Zurgo Bellstriker", ""));
cubeCards.add(new CardIdentity("Abbot of Keral Keep", ""));
cubeCards.add(new CardIdentity("Dire Fleet Daredevil", ""));
cubeCards.add(new CardIdentity("Eidolon of the Great Revel", ""));
cubeCards.add(new CardIdentity("Runaway Steam-Kin", ""));
cubeCards.add(new CardIdentity("Young Pyromancer", ""));
cubeCards.add(new CardIdentity("Goblin Rabblemaster", ""));
cubeCards.add(new CardIdentity("Imperial Recruiter", ""));
cubeCards.add(new CardIdentity("Magus of the Moon", ""));
cubeCards.add(new CardIdentity("Avalanche Riders", ""));
cubeCards.add(new CardIdentity("Flametongue Kavu", ""));
cubeCards.add(new CardIdentity("Hazoret the Fervent", ""));
cubeCards.add(new CardIdentity("Hellrider", ""));
cubeCards.add(new CardIdentity("Pia and Kiran Nalaar", ""));
cubeCards.add(new CardIdentity("Rekindling Phoenix", ""));
cubeCards.add(new CardIdentity("Glorybringer", ""));
cubeCards.add(new CardIdentity("Goblin Dark-Dwellers", ""));
cubeCards.add(new CardIdentity("Kiki-Jiki, Mirror Breaker", ""));
cubeCards.add(new CardIdentity("Siege-Gang Commander", ""));
cubeCards.add(new CardIdentity("Thundermaw Hellkite", ""));
cubeCards.add(new CardIdentity("Zealous Conscripts", ""));
cubeCards.add(new CardIdentity("Inferno Titan", ""));
cubeCards.add(new CardIdentity("Chandra, Torch of Defiance", ""));
cubeCards.add(new CardIdentity("Daretti, Scrap Savant", ""));
cubeCards.add(new CardIdentity("Koth of the Hammer", ""));
cubeCards.add(new CardIdentity("Burst Lightning", ""));
cubeCards.add(new CardIdentity("Lightning Bolt", ""));
cubeCards.add(new CardIdentity("Abrade", ""));
cubeCards.add(new CardIdentity("Ancient Grudge", ""));
cubeCards.add(new CardIdentity("Desperate Ritual", ""));
cubeCards.add(new CardIdentity("Fire // Ice", ""));
cubeCards.add(new CardIdentity("Incinerate", ""));
cubeCards.add(new CardIdentity("Lightning Strike", ""));
cubeCards.add(new CardIdentity("Pyretic Ritual", ""));
cubeCards.add(new CardIdentity("Char", ""));
cubeCards.add(new CardIdentity("Seething Song", ""));
cubeCards.add(new CardIdentity("Through the Breach", ""));
cubeCards.add(new CardIdentity("Fireblast", ""));
cubeCards.add(new CardIdentity("Chain Lightning", ""));
cubeCards.add(new CardIdentity("Faithless Looting", ""));
cubeCards.add(new CardIdentity("Firebolt", ""));
cubeCards.add(new CardIdentity("Flame Slash", ""));
cubeCards.add(new CardIdentity("Mizzium Mortars", ""));
cubeCards.add(new CardIdentity("Pyroclasm", ""));
cubeCards.add(new CardIdentity("Light Up the Stage", ""));
cubeCards.add(new CardIdentity("Underworld Breach", ""));
cubeCards.add(new CardIdentity("Wheel of Fortune", ""));
cubeCards.add(new CardIdentity("Empty the Warrens", ""));
cubeCards.add(new CardIdentity("Fiery Confluence", ""));
cubeCards.add(new CardIdentity("Past in Flames", ""));
cubeCards.add(new CardIdentity("Banefire", ""));
cubeCards.add(new CardIdentity("Burning of Xinye", ""));
cubeCards.add(new CardIdentity("Wildfire", ""));
cubeCards.add(new CardIdentity("Bonfire of the Damned", ""));
cubeCards.add(new CardIdentity("Mana Flare", ""));
cubeCards.add(new CardIdentity("Sulfuric Vortex", ""));
cubeCards.add(new CardIdentity("Sneak Attack", ""));
cubeCards.add(new CardIdentity("Splinter Twin", ""));
cubeCards.add(new CardIdentity("Arbor Elf", ""));
cubeCards.add(new CardIdentity("Avacyn's Pilgrim", ""));
cubeCards.add(new CardIdentity("Birds of Paradise", ""));
cubeCards.add(new CardIdentity("Elves of Deep Shadow", ""));
cubeCards.add(new CardIdentity("Elvish Mystic", ""));
cubeCards.add(new CardIdentity("Fyndhorn Elves", ""));
cubeCards.add(new CardIdentity("Joraga Treespeaker", ""));
cubeCards.add(new CardIdentity("Llanowar Elves", ""));
cubeCards.add(new CardIdentity("Noble Hierarch", ""));
cubeCards.add(new CardIdentity("Den Protector", ""));
cubeCards.add(new CardIdentity("Devoted Druid", ""));
cubeCards.add(new CardIdentity("Fauna Shaman", ""));
cubeCards.add(new CardIdentity("Gilded Goose", ""));
cubeCards.add(new CardIdentity("Lotus Cobra", ""));
cubeCards.add(new CardIdentity("Rofellos, Llanowar Emissary", ""));
cubeCards.add(new CardIdentity("Sakura-Tribe Elder", ""));
cubeCards.add(new CardIdentity("Scavenging Ooze", ""));
cubeCards.add(new CardIdentity("Sylvan Caryatid", ""));
cubeCards.add(new CardIdentity("Wall of Blossoms", ""));
cubeCards.add(new CardIdentity("Wall of Roots", ""));
cubeCards.add(new CardIdentity("Courser of Kruphix", ""));
cubeCards.add(new CardIdentity("Eternal Witness", ""));
cubeCards.add(new CardIdentity("Ramunap Excavator", ""));
cubeCards.add(new CardIdentity("Reclamation Sage", ""));
cubeCards.add(new CardIdentity("Tireless Tracker", ""));
cubeCards.add(new CardIdentity("Yavimaya Elder", ""));
cubeCards.add(new CardIdentity("Master of the Wild Hunt", ""));
cubeCards.add(new CardIdentity("Oracle of Mul Daya", ""));
cubeCards.add(new CardIdentity("Polukranos, World Eater", ""));
cubeCards.add(new CardIdentity("Acidic Slime", ""));
cubeCards.add(new CardIdentity("Biogenic Ooze", ""));
cubeCards.add(new CardIdentity("Deranged Hermit", ""));
cubeCards.add(new CardIdentity("Thragtusk", ""));
cubeCards.add(new CardIdentity("Whisperwood Elemental", ""));
cubeCards.add(new CardIdentity("Carnage Tyrant", ""));
cubeCards.add(new CardIdentity("Primeval Titan", ""));
cubeCards.add(new CardIdentity("Avenger of Zendikar", ""));
cubeCards.add(new CardIdentity("Craterhoof Behemoth", ""));
cubeCards.add(new CardIdentity("Terastodon", ""));
cubeCards.add(new CardIdentity("Woodfall Primus", ""));
cubeCards.add(new CardIdentity("Dryad of the Ilysian Grove", ""));
cubeCards.add(new CardIdentity("Garruk Relentless", ""));
cubeCards.add(new CardIdentity("Garruk Wildspeaker", ""));
cubeCards.add(new CardIdentity("Garruk, Primal Hunter", ""));
cubeCards.add(new CardIdentity("Vivien Reid", ""));
cubeCards.add(new CardIdentity("Nature's Claim", ""));
cubeCards.add(new CardIdentity("Beast Within", ""));
cubeCards.add(new CardIdentity("Channel", ""));
cubeCards.add(new CardIdentity("Regrowth", ""));
cubeCards.add(new CardIdentity("Kodama's Reach", ""));
cubeCards.add(new CardIdentity("Search for Tomorrow", ""));
cubeCards.add(new CardIdentity("Eureka", ""));
cubeCards.add(new CardIdentity("Harmonize", ""));
cubeCards.add(new CardIdentity("Natural Order", ""));
cubeCards.add(new CardIdentity("Plow Under", ""));
cubeCards.add(new CardIdentity("Primal Command", ""));
cubeCards.add(new CardIdentity("Green Sun's Zenith", ""));
cubeCards.add(new CardIdentity("Finale of Devastation", ""));
cubeCards.add(new CardIdentity("Tooth and Nail", ""));
cubeCards.add(new CardIdentity("Fastbond", ""));
cubeCards.add(new CardIdentity("Oath of Druids", ""));
cubeCards.add(new CardIdentity("Survival of the Fittest", ""));
cubeCards.add(new CardIdentity("Sylvan Library", ""));
cubeCards.add(new CardIdentity("Heartbeat of Spring", ""));
cubeCards.add(new CardIdentity("Wilderness Reclamation", ""));
cubeCards.add(new CardIdentity("Gaea's Cradle", ""));
cubeCards.add(new CardIdentity("Geist of Saint Traft", ""));
cubeCards.add(new CardIdentity("Teferi, Hero of Dominaria", ""));
cubeCards.add(new CardIdentity("Sphinx's Revelation", ""));
cubeCards.add(new CardIdentity("Fractured Identity", ""));
cubeCards.add(new CardIdentity("Celestial Colonnade", ""));
cubeCards.add(new CardIdentity("Flooded Strand", ""));
cubeCards.add(new CardIdentity("Hallowed Fountain", ""));
cubeCards.add(new CardIdentity("Seachrome Coast", ""));
cubeCards.add(new CardIdentity("Tundra", ""));
cubeCards.add(new CardIdentity("Thief of Sanity", ""));
cubeCards.add(new CardIdentity("The Scarab God", ""));
cubeCards.add(new CardIdentity("Ashiok, Nightmare Weaver", ""));
cubeCards.add(new CardIdentity("Baleful Strix", ""));
cubeCards.add(new CardIdentity("Creeping Tar Pit", ""));
cubeCards.add(new CardIdentity("Darkslick Shores", ""));
cubeCards.add(new CardIdentity("Polluted Delta", ""));
cubeCards.add(new CardIdentity("Underground Sea", ""));
cubeCards.add(new CardIdentity("Watery Grave", ""));
cubeCards.add(new CardIdentity("Daretti, Ingenious Iconoclast", ""));
cubeCards.add(new CardIdentity("Kroxa, Titan of Death's Hunger", ""));
cubeCards.add(new CardIdentity("Kolaghan's Command", ""));
cubeCards.add(new CardIdentity("Rakdos's Return", ""));
cubeCards.add(new CardIdentity("Badlands", ""));
cubeCards.add(new CardIdentity("Blackcleave Cliffs", ""));
cubeCards.add(new CardIdentity("Blood Crypt", ""));
cubeCards.add(new CardIdentity("Bloodstained Mire", ""));
cubeCards.add(new CardIdentity("Lavaclaw Reaches", ""));
cubeCards.add(new CardIdentity("Bloodbraid Elf", ""));
cubeCards.add(new CardIdentity("Huntmaster of the Fells", ""));
cubeCards.add(new CardIdentity("Dragonlord Atarka", ""));
cubeCards.add(new CardIdentity("Manamorphose", ""));
cubeCards.add(new CardIdentity("Copperline Gorge", ""));
cubeCards.add(new CardIdentity("Raging Ravine", ""));
cubeCards.add(new CardIdentity("Stomping Ground", ""));
cubeCards.add(new CardIdentity("Taiga", ""));
cubeCards.add(new CardIdentity("Wooded Foothills", ""));
cubeCards.add(new CardIdentity("Kitchen Finks", ""));
cubeCards.add(new CardIdentity("Knight of Autumn", ""));
cubeCards.add(new CardIdentity("Knight of the Reliquary", ""));
cubeCards.add(new CardIdentity("Trostani Discordant", ""));
cubeCards.add(new CardIdentity("Mirari's Wake", ""));
cubeCards.add(new CardIdentity("Razorverge Thicket", ""));
cubeCards.add(new CardIdentity("Savannah", ""));
cubeCards.add(new CardIdentity("Stirring Wildwood", ""));
cubeCards.add(new CardIdentity("Temple Garden", ""));
cubeCards.add(new CardIdentity("Windswept Heath", ""));
cubeCards.add(new CardIdentity("Ashen Rider", ""));
cubeCards.add(new CardIdentity("Kaya, Orzhov Usurper", ""));
cubeCards.add(new CardIdentity("Tidehollow Sculler", ""));
cubeCards.add(new CardIdentity("Anguished Unmaking", ""));
cubeCards.add(new CardIdentity("Lingering Souls", ""));
cubeCards.add(new CardIdentity("Vindicate", ""));
cubeCards.add(new CardIdentity("Unburial Rites", ""));
cubeCards.add(new CardIdentity("Concealed Courtyard", ""));
cubeCards.add(new CardIdentity("Godless Shrine", ""));
cubeCards.add(new CardIdentity("Marsh Flats", ""));
cubeCards.add(new CardIdentity("Scrubland", ""));
cubeCards.add(new CardIdentity("Shambling Vent", ""));
cubeCards.add(new CardIdentity("Vraska, Golgari Queen", ""));
cubeCards.add(new CardIdentity("Assassin's Trophy", ""));
cubeCards.add(new CardIdentity("Maelstrom Pulse", ""));
cubeCards.add(new CardIdentity("Pernicious Deed", ""));
cubeCards.add(new CardIdentity("Bayou", ""));
cubeCards.add(new CardIdentity("Blooming Marsh", ""));
cubeCards.add(new CardIdentity("Hissing Quagmire", ""));
cubeCards.add(new CardIdentity("Overgrown Tomb", ""));
cubeCards.add(new CardIdentity("Verdant Catacombs", ""));
cubeCards.add(new CardIdentity("Edric, Spymaster of Trest", ""));
cubeCards.add(new CardIdentity("Trygon Predator", ""));
cubeCards.add(new CardIdentity("Hydroid Krasis", ""));
cubeCards.add(new CardIdentity("Uro, Titan of Nature's Wrath", ""));
cubeCards.add(new CardIdentity("Botanical Sanctum", ""));
cubeCards.add(new CardIdentity("Breeding Pool", ""));
cubeCards.add(new CardIdentity("Lumbering Falls", ""));
cubeCards.add(new CardIdentity("Misty Rainforest", ""));
cubeCards.add(new CardIdentity("Tropical Island", ""));
cubeCards.add(new CardIdentity("Goblin Electromancer", ""));
cubeCards.add(new CardIdentity("Dack Fayden", ""));
cubeCards.add(new CardIdentity("Thousand-Year Storm", ""));
cubeCards.add(new CardIdentity("Scalding Tarn", ""));
cubeCards.add(new CardIdentity("Spirebluff Canal", ""));
cubeCards.add(new CardIdentity("Steam Vents", ""));
cubeCards.add(new CardIdentity("Volcanic Island", ""));
cubeCards.add(new CardIdentity("Wandering Fumarole", ""));
cubeCards.add(new CardIdentity("Figure of Destiny", ""));
cubeCards.add(new CardIdentity("Ajani Vengeant", ""));
cubeCards.add(new CardIdentity("Nahiri, the Harbinger", ""));
cubeCards.add(new CardIdentity("Wear // Tear", ""));
cubeCards.add(new CardIdentity("Lightning Helix", ""));
cubeCards.add(new CardIdentity("Arid Mesa", ""));
cubeCards.add(new CardIdentity("Inspiring Vantage", ""));
cubeCards.add(new CardIdentity("Needle Spires", ""));
cubeCards.add(new CardIdentity("Plateau", ""));
cubeCards.add(new CardIdentity("Sacred Foundry", ""));
cubeCards.add(new CardIdentity("Sphinx of the Steel Wind", ""));
cubeCards.add(new CardIdentity("Nicol Bolas, Dragon-God", ""));
cubeCards.add(new CardIdentity("Leovold, Emissary of Trest", ""));
cubeCards.add(new CardIdentity("Progenitus", ""));
cubeCards.add(new CardIdentity("Kozilek, Butcher of Truth", ""));
cubeCards.add(new CardIdentity("Ulamog, the Ceaseless Hunger", ""));
cubeCards.add(new CardIdentity("Ulamog, the Infinite Gyre", ""));
cubeCards.add(new CardIdentity("Emrakul, the Promised End", ""));
cubeCards.add(new CardIdentity("Emrakul, the Aeons Torn", ""));
cubeCards.add(new CardIdentity("Karn, Scion of Urza", ""));
cubeCards.add(new CardIdentity("Karn Liberated", ""));
cubeCards.add(new CardIdentity("Ugin, the Spirit Dragon", ""));
cubeCards.add(new CardIdentity("Bomat Courier", ""));
cubeCards.add(new CardIdentity("Hangarback Walker", ""));
cubeCards.add(new CardIdentity("Phyrexian Revoker", ""));
cubeCards.add(new CardIdentity("Metalworker", ""));
cubeCards.add(new CardIdentity("Lodestone Golem", ""));
cubeCards.add(new CardIdentity("Solemn Simulacrum", ""));
cubeCards.add(new CardIdentity("Kuldotha Forgemaster", ""));
cubeCards.add(new CardIdentity("Wurmcoil Engine", ""));
cubeCards.add(new CardIdentity("Myr Battlesphere", ""));
cubeCards.add(new CardIdentity("Sundering Titan", ""));
cubeCards.add(new CardIdentity("Walking Ballista", ""));
cubeCards.add(new CardIdentity("Blightsteel Colossus", ""));
cubeCards.add(new CardIdentity("Black Lotus", ""));
cubeCards.add(new CardIdentity("Chrome Mox", ""));
cubeCards.add(new CardIdentity("Everflowing Chalice", ""));
cubeCards.add(new CardIdentity("Lion's Eye Diamond", ""));
cubeCards.add(new CardIdentity("Lotus Bloom", ""));
cubeCards.add(new CardIdentity("Mana Crypt", ""));
cubeCards.add(new CardIdentity("Mox Diamond", ""));
cubeCards.add(new CardIdentity("Mox Emerald", ""));
cubeCards.add(new CardIdentity("Mox Jet", ""));
cubeCards.add(new CardIdentity("Mox Pearl", ""));
cubeCards.add(new CardIdentity("Mox Ruby", ""));
cubeCards.add(new CardIdentity("Mox Sapphire", ""));
cubeCards.add(new CardIdentity("Mana Vault", ""));
cubeCards.add(new CardIdentity("Relic of Progenitus", ""));
cubeCards.add(new CardIdentity("Sensei's Divining Top", ""));
cubeCards.add(new CardIdentity("Skullclamp", ""));
cubeCards.add(new CardIdentity("Sol Ring", ""));
cubeCards.add(new CardIdentity("Azorius Signet", ""));
cubeCards.add(new CardIdentity("Boros Signet", ""));
cubeCards.add(new CardIdentity("Dimir Signet", ""));
cubeCards.add(new CardIdentity("Golgari Signet", ""));
cubeCards.add(new CardIdentity("Grim Monolith", ""));
cubeCards.add(new CardIdentity("Gruul Signet", ""));
cubeCards.add(new CardIdentity("Izzet Signet", ""));
cubeCards.add(new CardIdentity("Lightning Greaves", ""));
cubeCards.add(new CardIdentity("Orzhov Signet", ""));
cubeCards.add(new CardIdentity("Rakdos Signet", ""));
cubeCards.add(new CardIdentity("Selesnya Signet", ""));
cubeCards.add(new CardIdentity("Shrine of Burning Rage", ""));
cubeCards.add(new CardIdentity("Simic Signet", ""));
cubeCards.add(new CardIdentity("Smuggler's Copter", ""));
cubeCards.add(new CardIdentity("Umezawa's Jitte", ""));
cubeCards.add(new CardIdentity("Winter Orb", ""));
cubeCards.add(new CardIdentity("Basalt Monolith", ""));
cubeCards.add(new CardIdentity("Coalition Relic", ""));
cubeCards.add(new CardIdentity("Crucible of Worlds", ""));
cubeCards.add(new CardIdentity("Oblivion Stone", ""));
cubeCards.add(new CardIdentity("Sword of Body and Mind", ""));
cubeCards.add(new CardIdentity("Sword of Feast and Famine", ""));
cubeCards.add(new CardIdentity("Sword of Fire and Ice", ""));
cubeCards.add(new CardIdentity("Sword of Light and Shadow", ""));
cubeCards.add(new CardIdentity("Sword of War and Peace", ""));
cubeCards.add(new CardIdentity("Tangle Wire", ""));
cubeCards.add(new CardIdentity("Worn Powerstone", ""));
cubeCards.add(new CardIdentity("Coercive Portal", ""));
cubeCards.add(new CardIdentity("Smokestack", ""));
cubeCards.add(new CardIdentity("Thran Dynamo", ""));
cubeCards.add(new CardIdentity("Batterskull", ""));
cubeCards.add(new CardIdentity("Memory Jar", ""));
cubeCards.add(new CardIdentity("Mindslaver", ""));
cubeCards.add(new CardIdentity("Academy Ruins", ""));
cubeCards.add(new CardIdentity("Ancient Tomb", ""));
cubeCards.add(new CardIdentity("Bazaar of Baghdad", ""));
cubeCards.add(new CardIdentity("Blast Zone", ""));
cubeCards.add(new CardIdentity("Field of Ruin", ""));
cubeCards.add(new CardIdentity("Library of Alexandria", ""));
cubeCards.add(new CardIdentity("Maze of Ith", ""));
cubeCards.add(new CardIdentity("Mishra's Factory", ""));
cubeCards.add(new CardIdentity("Mishra's Workshop", ""));
cubeCards.add(new CardIdentity("Mutavault", ""));
cubeCards.add(new CardIdentity("Nykthos, Shrine to Nyx", ""));
cubeCards.add(new CardIdentity("Rishadan Port", ""));
cubeCards.add(new CardIdentity("Strip Mine", ""));
cubeCards.add(new CardIdentity("Wasteland", ""));
cubeCards.add(new CardIdentity("Expansion // Explosion", ""));
cubeCards.add(new CardIdentity("Giver of Runes", ""));
cubeCards.add(new CardIdentity("Winds of Abandon", ""));
cubeCards.add(new CardIdentity("Hallowed Spiritkeeper", ""));
cubeCards.add(new CardIdentity("Thraben Inspector", ""));
cubeCards.add(new CardIdentity("Narset, Parter of Veils", ""));
cubeCards.add(new CardIdentity("Force of Negation", ""));
cubeCards.add(new CardIdentity("Urza, Lord High Artificer", ""));
cubeCards.add(new CardIdentity("Emry, Lurker of the Loch", ""));
cubeCards.add(new CardIdentity("Brazen Borrower", ""));
cubeCards.add(new CardIdentity("Bolas's Citadel", ""));
cubeCards.add(new CardIdentity("Yawgmoth, Thran Physician", ""));
cubeCards.add(new CardIdentity("Rotting Regisaur", ""));
cubeCards.add(new CardIdentity("Murderous Rider", ""));
cubeCards.add(new CardIdentity("Wishclaw Talisman", ""));
cubeCards.add(new CardIdentity("Dreadhorde Arcanist", ""));
cubeCards.add(new CardIdentity("Seasoned Pyromancer", ""));
cubeCards.add(new CardIdentity("Embereth Shieldbreaker", ""));
cubeCards.add(new CardIdentity("Nissa, Who Shakes the World", ""));
cubeCards.add(new CardIdentity("Questing Beast", ""));
cubeCards.add(new CardIdentity("Teferi, Time Raveler", ""));
cubeCards.add(new CardIdentity("Angrath's Rampage", ""));
cubeCards.add(new CardIdentity("Fallen Shinobi", ""));
cubeCards.add(new CardIdentity("Wrenn and Six", ""));
cubeCards.add(new CardIdentity("Oko, Thief of Crowns", ""));
cubeCards.add(new CardIdentity("Garruk, Cursed Huntsman", ""));
cubeCards.add(new CardIdentity("Prismatic Vista", ""));
cubeCards.add(new CardIdentity("Golos, Tireless Pilgrim", ""));
cubeCards.add(new CardIdentity("Stonecoil Serpent", ""));
}
}

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-tournament-constructed</artifactId>

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-server-plugins</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-tournament-sealed</artifactId>

View file

@ -0,0 +1,44 @@
package mage.tournament;
import mage.game.tournament.TournamentOptions;
import mage.game.tournament.TournamentSingleElimination;
public class JumpstartEliminationTournament extends TournamentSingleElimination {
protected enum TournamentStep {
START, OPEN_BOOSTERS, CONSTRUCT, COMPETE, WINNERS
}
protected TournamentStep currentStep;
public JumpstartEliminationTournament(TournamentOptions options) {
super(options);
currentStep = TournamentStep.START;
}
@Override
public void nextStep() {
switch (currentStep) {
case START:
currentStep = TournamentStep.OPEN_BOOSTERS;
openBoosters();
break;
case OPEN_BOOSTERS:
currentStep = TournamentStep.CONSTRUCT;
construct();
break;
case CONSTRUCT:
currentStep = TournamentStep.COMPETE;
runTournament();
break;
case COMPETE:
currentStep = TournamentStep.WINNERS;
winners();
end();
break;
}
}
}

View file

@ -0,0 +1,19 @@
package mage.tournament;
import mage.game.tournament.TournamentType;
public class JumpstartEliminationTournamentType extends TournamentType {
public JumpstartEliminationTournamentType() {
this.name = "Jumpstart Elimination";
this.maxPlayers = 16;
this.minPlayers = 2;
this.numBoosters = 0;
this.isJumpstart = true;
this.elimination = true;
this.limited = true;
}
}

View file

@ -0,0 +1,44 @@
package mage.tournament;
import mage.game.tournament.TournamentOptions;
import mage.game.tournament.TournamentSwiss;
public class JumpstartSwissTournament extends TournamentSwiss {
protected enum TournamentStep {
START, OPEN_BOOSTERS, CONSTRUCT, COMPETE, WINNERS
}
protected TournamentStep currentStep;
public JumpstartSwissTournament(TournamentOptions options) {
super(options);
currentStep = TournamentStep.START;
}
@Override
public void nextStep() {
switch (currentStep) {
case START:
currentStep = TournamentStep.OPEN_BOOSTERS;
openBoosters();
break;
case OPEN_BOOSTERS:
currentStep = TournamentStep.CONSTRUCT;
construct();
break;
case CONSTRUCT:
currentStep = TournamentStep.COMPETE;
runTournament();
break;
case COMPETE:
currentStep = TournamentStep.WINNERS;
winners();
end();
break;
}
}
}

View file

@ -0,0 +1,18 @@
package mage.tournament;
import mage.game.tournament.TournamentType;
public class JumpstartSwissTournamentType extends TournamentType {
public JumpstartSwissTournamentType() {
this.name = "Jumpstart Swiss";
this.maxPlayers = 16;
this.minPlayers = 2;
this.numBoosters = 0;
this.isJumpstart = true;
this.limited = true;
}
}

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-root</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-server-plugins</artifactId>

View file

@ -56,13 +56,13 @@
saveGameActivated="false"
authenticationActivated="false"
googleAccount=""
mailgunApiKey="key-d93e81f19a9c9ed243ebb7cc9381385c"
mailgunDomain="sandbox401a433f30d445309a5e86b6c53f7812.mailgun.org"
mailSmtpHost="smtp.1und1.de"
mailSmtpPort="465"
mailUser="xmageserver@online.de"
mailPassword="24wrsfxv"
mailFromAddress="xmageserver@online.de"
mailgunApiKey=""
mailgunDomain=""
mailSmtpHost=""
mailSmtpPort=""
mailUser=""
mailPassword=""
mailFromAddress=""
/>
<playerTypes>
<playerType name="Human" jar="mage-player-human.jar" className="mage.player.human.HumanPlayer"/>
@ -104,6 +104,8 @@
<tournamentType name="Sealed Elimination (Cube)" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationCubeTournamentType"/>
<tournamentType name="Sealed Swiss" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissTournamentType"/>
<tournamentType name="Sealed Swiss (Cube)" jar="mage-tournament-sealed.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissCubeTournamentType"/>
<tournamentType name="Jumpstart Elimination" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartEliminationTournament" typeName="mage.tournament.JumpstartEliminationTournamentType"/>
<tournamentType name="Jumpstart Swiss" jar="mage-tournament-sealed.jar" className="mage.tournament.JumpstartSwissTournament" typeName="mage.tournament.JumpstartSwissTournamentType"/>
</tournamentTypes>
<draftCubes>
<draftCube name="Adam Styborski's Pauper Cube" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.AdamStyborskisPauperCube"/>
@ -143,6 +145,7 @@
<draftCube name="MTGO Vintage Cube December 2018" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeDecember2018"/>
<draftCube name="MTGO Vintage Cube June 2019" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeJune2019"/>
<draftCube name="MTGO Vintage Cube December 2019" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeDecember2019"/>
<draftCube name="MTGO Vintage Cube April 2020" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeApril2020"/>
<draftCube name="SCG Con Cube 2018 December" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.ScgConCube2018December"/>
<draftCube name="The Peasant's Toolbox" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.PeasantsToolboxCube"/>
<draftCube name="www.MTGCube.com" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.MTGCube"/>

View file

@ -6,7 +6,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-root</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-server</artifactId>

View file

@ -98,6 +98,8 @@
<tournamentType name="Sealed Elimination (Cube)" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedEliminationTournament" typeName="mage.tournament.SealedEliminationCubeTournamentType"/>
<tournamentType name="Sealed Swiss" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissTournamentType"/>
<tournamentType name="Sealed Swiss (Cube)" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.SealedSwissTournament" typeName="mage.tournament.SealedSwissCubeTournamentType"/>
<tournamentType name="Jumpstart Elimination" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.JumpstartEliminationTournament" typeName="mage.tournament.JumpstartEliminationTournamentType"/>
<tournamentType name="Jumpstart Swiss" jar="mage-tournament-sealed-${project.version}.jar" className="mage.tournament.JumpstartSwissTournament" typeName="mage.tournament.JumpstartSwissTournamentType"/>
</tournamentTypes>
<draftCubes>
<draftCube name="Adam Styborski's Pauper Cube" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.AdamStyborskisPauperCube"/>
@ -137,6 +139,7 @@
<draftCube name="MTGO Vintage Cube December 2018" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeDecember2018"/>
<draftCube name="MTGO Vintage Cube June 2019" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeJune2019"/>
<draftCube name="MTGO Vintage Cube December 2019" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeDecember2019"/>
<draftCube name="MTGO Vintage Cube April 2020" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.VintageCubeApril2020"/>
<draftCube name="SCG Con Cube 2018 December" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.ScgConCube2018December"/>
<draftCube name="The Peasant's Toolbox" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.PeasantsToolboxCube"/>
<draftCube name="www.MTGCube.com" jar="mage-tournament-booster-draft-${project.version}.jar" className="mage.tournament.cubes.MTGCube"/>

View file

@ -397,15 +397,15 @@ public class MageServerImpl implements MageServer {
@Override
//FIXME: why no sessionId here???
public Optional<TableView> getTable(UUID roomId, UUID tableId) throws MageException {
public TableView getTable(UUID roomId, UUID tableId) throws MageException {
try {
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
return room.flatMap(r -> r.getTable(tableId));
return room.flatMap(r -> r.getTable(tableId)).orElse(null);
} catch (Exception ex) {
handleException(ex);
}
return Optional.empty();
return null;
}
@Override
@ -536,18 +536,18 @@ public class MageServerImpl implements MageServer {
@Override
//FIXME: why no sessionId here???
public Optional<UUID> getRoomChatId(UUID roomId) throws MageException {
public UUID getRoomChatId(UUID roomId) throws MageException {
try {
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
if (!room.isPresent()) {
logger.error("roomId not found : " + roomId);
return Optional.empty();
return null;
}
return Optional.of(room.get().getChatId());
return room.get().getChatId();
} catch (Exception ex) {
handleException(ex);
}
return Optional.empty();
return null;
}
@Override
@ -602,13 +602,13 @@ public class MageServerImpl implements MageServer {
@Override
//FIXME: why no sessionId here???
public Optional<UUID> getTableChatId(UUID tableId) throws MageException {
public UUID getTableChatId(UUID tableId) throws MageException {
try {
return TableManager.instance.getChatId(tableId);
return TableManager.instance.getChatId(tableId).orElse(null);
} catch (Exception ex) {
handleException(ex);
}
return Optional.empty();
return null;
}
@Override
@ -646,24 +646,24 @@ public class MageServerImpl implements MageServer {
@Override
//FIXME: why no sessionId here???
public Optional<UUID> getGameChatId(UUID gameId) throws MageException {
public UUID getGameChatId(UUID gameId) throws MageException {
try {
return GameManager.instance.getChatId(gameId);
return GameManager.instance.getChatId(gameId).orElse(null);
} catch (Exception ex) {
handleException(ex);
}
return Optional.empty();
return null;
}
@Override
//FIXME: why no sessionId here???
public Optional<UUID> getTournamentChatId(UUID tournamentId) throws MageException {
public UUID getTournamentChatId(UUID tournamentId) throws MageException {
try {
return TournamentManager.instance.getChatId(tournamentId);
return TournamentManager.instance.getChatId(tournamentId).orElse(null);
} catch (Exception ex) {
handleException(ex);
}
return Optional.empty();
return null;
}
@Override

View file

@ -1259,10 +1259,10 @@ public class GameController implements GameCallback {
.collect(Collectors.joining(", ")));
StringBuilder sb = new StringBuilder();
sb.append("<font color='red'>FIX command called by " + user.getName() + "</font>");
sb.append("<font color='red'>FIX command called by ").append(user.getName()).append("</font>");
sb.append("<font size='-2'>"); // font resize start for all next logs
sb.append("<br>Game ID: " + game.getId());
sb.append("<br>Game ID: ").append(game.getId());
sb.append("<br>Phase: ").append(game.getTurn().getPhaseType().toString()).append(" Step: ").append(game.getTurn().getStepType().toString());
// pings info
sb.append("<br>");
sb.append(getPingsInfo());
@ -1272,7 +1272,7 @@ public class GameController implements GameCallback {
// fix active
Player playerActive = game.getPlayer(state.getActivePlayerId());
sb.append("<br>Fixing active player: " + getName(playerActive));
sb.append("<br>Fixing active player: ").append(getName(playerActive));
if (playerActive != null && !playerActive.canRespond()) {
fixActions.add("active player fix");
@ -1280,16 +1280,16 @@ public class GameController implements GameCallback {
sb.append("<br>Try to concede...");
playerActive.concede(game);
playerActive.leave(); // abort any wait response actions
sb.append(" (" + asWarning("OK") + ", concede done)");
sb.append(" (").append(asWarning("OK")).append(", concede done)");
sb.append("<br>Try to skip step...");
Phase currentPhase = game.getPhase();
if (currentPhase != null) {
currentPhase.getStep().skipStep(game, state.getActivePlayerId());
fixedAlready = true;
sb.append(" (" + asWarning("OK") + ", skip step done)");
sb.append(" (").append(asWarning("OK")).append(", skip step done)");
} else {
sb.append(" (" + asBad("FAIL") + ", step is null)");
sb.append(" (").append(asBad("FAIL")).append(", step is null)");
}
} else {
sb.append(playerActive != null ? " (" + asGood("OK") + ", can respond)" : " (" + asGood("OK") + ", no player)");
@ -1297,7 +1297,7 @@ public class GameController implements GameCallback {
// fix lost choosing dialog
Player choosingPlayer = game.getPlayer(state.getChoosingPlayerId());
sb.append("<br>Fixing choosing player: " + getName(choosingPlayer));
sb.append("<br>Fixing choosing player: ").append(getName(choosingPlayer));
if (choosingPlayer != null && !choosingPlayer.canRespond()) {
fixActions.add("choosing player fix");
@ -1305,7 +1305,7 @@ public class GameController implements GameCallback {
sb.append("<br>Try to concede...");
choosingPlayer.concede(game);
choosingPlayer.leave(); // abort any wait response actions
sb.append(" (" + asWarning("OK") + ", concede done)");
sb.append(" (").append(asWarning("OK")).append(", concede done)");
sb.append("<br>Try to skip step...");
if (fixedAlready) {
@ -1315,9 +1315,9 @@ public class GameController implements GameCallback {
if (currentPhase != null) {
currentPhase.getStep().skipStep(game, state.getActivePlayerId());
fixedAlready = true;
sb.append(" (" + asWarning("OK") + ", skip step done)");
sb.append(" (").append(asWarning("OK")).append(", skip step done)");
} else {
sb.append(" (" + asBad("FAIL") + ", step is null)");
sb.append(" (").append(asBad("FAIL")).append(", step is null)");
}
}
} else {
@ -1326,7 +1326,7 @@ public class GameController implements GameCallback {
// fix lost priority
Player priorityPlayer = game.getPlayer(state.getPriorityPlayerId());
sb.append("<br>Fixing priority player: " + getName(priorityPlayer));
sb.append("<br>Fixing priority player: ").append(getName(priorityPlayer));
if (priorityPlayer != null && !priorityPlayer.canRespond()) {
fixActions.add("priority player fix");
@ -1334,19 +1334,19 @@ public class GameController implements GameCallback {
sb.append("<br>Try to concede...");
priorityPlayer.concede(game);
priorityPlayer.leave(); // abort any wait response actions
sb.append(" (" + asWarning("OK") + ", concede done)");
sb.append(" (").append(asWarning("OK")).append(", concede done)");
sb.append("<br>Try to skip step...");
if (fixedAlready) {
sb.append(" (" + asWarning("OK") + ", already skipped before)");
sb.append(" (").append(asWarning("OK")).append(", already skipped before)");
} else {
Phase currentPhase = game.getPhase();
if (currentPhase != null) {
currentPhase.getStep().skipStep(game, state.getActivePlayerId());
fixedAlready = true;
sb.append(" (" + asWarning("OK") + ", skip step done)");
sb.append(" (").append(asWarning("OK")).append(", skip step done)");
} else {
sb.append(" (" + asBad("FAIL") + ", step is null)");
sb.append(" (").append(asBad("FAIL")).append(", step is null)");
}
}
} else {
@ -1356,10 +1356,10 @@ public class GameController implements GameCallback {
// fix timeout
sb.append("<br>Fixing future timeout: ");
if (futureTimeout != null) {
sb.append("cancelled?=" + futureTimeout.isCancelled());
sb.append("...done?=" + futureTimeout.isDone());
sb.append("cancelled?=").append(futureTimeout.isCancelled());
sb.append("...done?=").append(futureTimeout.isDone());
int delay = (int) futureTimeout.getDelay(TimeUnit.SECONDS);
sb.append("...getDelay?=" + delay);
sb.append("...getDelay?=").append(delay);
if (delay < 25) {
fixActions.add("future timeout fix");
@ -1367,12 +1367,12 @@ public class GameController implements GameCallback {
sb.append("<br>Try to pass...");
PassAbility pass = new PassAbility();
game.endTurn(pass);
sb.append(" (" + asWarning("OK") + ", pass done)");
sb.append(" (").append(asWarning("OK")).append(", pass done)");
} else {
sb.append(" (" + asGood("OK") + ", delay > 25)");
sb.append(" (").append(asGood("OK")).append(", delay > 25)");
}
} else {
sb.append(" (" + asGood("OK") + ", timeout is not using)");
sb.append(" (").append(asGood("OK")).append(", timeout is not using)");
}
// TODO: fix non started game (send game started event to user?)
@ -1382,7 +1382,7 @@ public class GameController implements GameCallback {
fixActions.add("none");
}
String appliedFixes = fixActions.stream().collect(Collectors.joining(", "));
sb.append("<br>Applied fixes: " + appliedFixes);
sb.append("<br>Applied fixes: ").append(appliedFixes);
sb.append("</font>"); // font resize end
sb.append("<br>");

View file

@ -82,7 +82,7 @@ public final class SystemUtil {
}
private static final Pattern patternGroup = Pattern.compile("\\[(.+)\\]"); // [test new card]
private static final Pattern patternCommand = Pattern.compile("([\\w]+):([\\S]+?):([\\S ]+):([\\d]+)"); // battlefield:Human:Island:10
private static final Pattern patternCommand = Pattern.compile("([\\w]+):([\\S ]+?):([\\S ]+):([\\d]+)"); // battlefield:Human:Island:10
private static final Pattern patternCardInfo = Pattern.compile("([\\S ]+):([\\S ]+)"); // Island:XLN
// show ext info for special commands
@ -425,7 +425,7 @@ public final class SystemUtil {
game.firePriorityEvent(opponent.getId());
}
List<Ability> abilities = opponent.getPlayable(game, true);
List<ActivatedAbility> abilities = opponent.getPlayable(game, true);
Map<String, String> choices = new HashMap<>();
abilities.forEach(ability -> {
MageObject object = ability.getSourceObject(game);
@ -437,10 +437,10 @@ public final class SystemUtil {
choice.setKeyChoices(choices);
if (feedbackPlayer.choose(Outcome.Detriment, choice, game) && choice.getChoiceKey() != null) {
String needId = choice.getChoiceKey();
Optional<Ability> ability = abilities.stream().filter(a -> a.getId().toString().equals(needId)).findFirst();
Optional<ActivatedAbility> ability = abilities.stream().filter(a -> a.getId().toString().equals(needId)).findFirst();
if (ability.isPresent()) {
// TODO: set priority for player?
ActivatedAbility activatedAbility = (ActivatedAbility) ability.get();
ActivatedAbility activatedAbility = ability.get();
game.informPlayers(feedbackPlayer.getLogName() + " as another player " + opponent.getLogName()
+ " trying to force an activate ability: " + activatedAbility.getGameLogMessage(game));
if (opponent.activateAbility(activatedAbility, game)) {

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-root</artifactId>
<version>1.4.42</version>
<version>1.4.43</version>
</parent>
<artifactId>mage-sets</artifactId>

View file

@ -1,4 +1,3 @@
package mage.cards.a;
import mage.abilities.Ability;
@ -15,8 +14,8 @@ import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.target.TargetPlayer;
import mage.target.common.TargetCardInHand;
import mage.target.common.TargetOpponent;
import java.util.UUID;
@ -29,18 +28,21 @@ public final class AbandonHope extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{1}{B}");
// As an additional cost to cast Abandon Hope, discard X cards.
Ability ability = new SimpleStaticAbility(Zone.ALL, new InfoEffect("As an additional cost to cast this spell, discard X cards"));
Ability ability = new SimpleStaticAbility(
Zone.ALL, new InfoEffect("As an additional cost to cast this spell, discard X cards")
);
ability.setRuleAtTheTop(true);
this.addAbility(ability);
// Look at target opponent's hand and choose X cards from it. That player discards those cards.
ManacostVariableValue manaX = ManacostVariableValue.instance;
this.getSpellAbility().addEffect(new DiscardCardYouChooseTargetEffect(manaX, TargetController.ANY));
this.getSpellAbility().addTarget(new TargetPlayer());
this.getSpellAbility().addEffect(
new DiscardCardYouChooseTargetEffect(ManacostVariableValue.instance, TargetController.ANY)
.setText("Look at target opponent's hand and choose X cards from it. That player discards those cards"));
this.getSpellAbility().addTarget(new TargetOpponent());
this.getSpellAbility().setCostAdjuster(AbandonHopeAdjuster.instance);
}
public AbandonHope(final AbandonHope card) {
private AbandonHope(final AbandonHope card) {
super(card);
}
@ -60,4 +62,4 @@ enum AbandonHopeAdjuster implements CostAdjuster {
ability.addCost(new DiscardTargetCost(new TargetCardInHand(xValue, xValue, StaticFilters.FILTER_CARD_CARDS)));
}
}
}
}

View file

@ -1,22 +1,22 @@
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.TransformSourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.TransformAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID;
/**
* @author fireshoes
*/
@ -54,7 +54,7 @@ class AberrantResearcherEffect extends OneShotEffect {
public AberrantResearcherEffect() {
super(Outcome.Benefit);
staticText = "put the top card of your library into your graveyard. If it's an instant or sorcery card, transform {this}";
staticText = "mill a card. If an instant or sorcery card was milled this way, transform {this}";
}
public AberrantResearcherEffect(final AberrantResearcherEffect effect) {
@ -64,15 +64,16 @@ class AberrantResearcherEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null && controller.getLibrary().hasCards()) {
Card card = controller.getLibrary().getFromTop(game);
controller.moveCards(card, Zone.GRAVEYARD, source, game);
if (card.isInstant() || card.isSorcery()) {
new TransformSourceEffect(true).apply(game, source);
}
return true;
if (controller == null
|| controller
.millCards(1, source, game)
.getCards(game)
.stream()
.noneMatch(MageObject::isInstantOrSorcery)) {
return false;
}
return false;
new TransformSourceEffect(true).apply(game, source);
return true;
}
@Override

View file

@ -1,7 +1,7 @@
package mage.cards.a;
import java.util.UUID;
import mage.abilities.common.DiesTriggeredAbility;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
@ -26,7 +26,7 @@ public final class AbnormalEndurance extends CardImpl {
.setText("Until end of turn, target creature gets +2/+0")
);
getSpellAbility().addEffect(new GainAbilityTargetEffect(
new DiesTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect(true, true), false),
new DiesSourceTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect(true, true), false),
Duration.EndOfTurn,
"and gains \"When this creature dies, return it to the battlefield tapped under its owner's control.\""
));

View file

@ -1,4 +1,3 @@
package mage.cards.a;
import mage.abilities.costs.AlternativeCostSourceAbility;
@ -16,7 +15,6 @@ import mage.target.common.TargetCardInHand;
import java.util.UUID;
/**
*
* @author Backfir3
*/
public final class Abolish extends CardImpl {
@ -28,8 +26,7 @@ public final class Abolish extends CardImpl {
}
public Abolish(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}{W}");
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}{W}");
// You may discard a Plains card rather than pay Abolish's mana cost.
this.addAbility(new AlternativeCostSourceAbility(new DiscardTargetCost(new TargetCardInHand(filterCost))));

View file

@ -4,7 +4,7 @@ package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.ObjectColor;
import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
import mage.abilities.common.BlocksOrBecomesBlockedSourceTriggeredAbility;
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@ -39,7 +39,7 @@ public final class Abomination extends CardImpl {
// Whenever Abomination blocks or becomes blocked by a green or white creature, destroy that creature at end of combat.
Effect effect = new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()), true);
effect.setText("destroy that creature at end of combat");
this.addAbility(new BlocksOrBecomesBlockedTriggeredAbility(effect, filter, false));
this.addAbility(new BlocksOrBecomesBlockedSourceTriggeredAbility(effect, filter, false));
}
public Abomination(final Abomination card) {

View file

@ -3,7 +3,7 @@ package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.DiesTriggeredAbility;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.effects.common.DestroyAllEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
@ -31,7 +31,7 @@ public final class AbuJafar extends CardImpl {
new BlockingAttackerIdPredicate(this.getId())));
// When Abu Ja'far dies, destroy all creatures blocking or blocked by it. They can't be regenerated.
this.addAbility(new DiesTriggeredAbility(new DestroyAllEffect(filter, true), false));
this.addAbility(new DiesSourceTriggeredAbility(new DestroyAllEffect(filter, true), false));
}
public AbuJafar(final AbuJafar card) {

View file

@ -1,7 +1,5 @@
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCostsImpl;
@ -15,14 +13,15 @@ import mage.constants.CardType;
import mage.constants.SubType;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
*
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/
public final class AbundantMaw extends CardImpl {
public AbundantMaw(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{8}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{8}");
this.subtype.add(SubType.ELDRAZI);
this.subtype.add(SubType.LEECH);
this.power = new MageInt(6);
@ -30,11 +29,11 @@ public final class AbundantMaw extends CardImpl {
// Emerge {6}{B}
this.addAbility(new EmergeAbility(this, new ManaCostsImpl<>("{6}{B}")));
// When you cast Abundant Maw, target opponent loses 3 life and you gain 3 life.
Ability ability = new CastSourceTriggeredAbility(new GainLifeEffect(3));
ability.addEffect(new LoseLifeTargetEffect(3).concatBy("and"));
ability.addTarget(new TargetOpponent());
ability.addEffect(new LoseLifeTargetEffect(3));
this.addAbility(ability);
}

View file

@ -3,7 +3,7 @@ package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.DiesTriggeredAbility;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.effects.common.SacrificeAllEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
@ -24,7 +24,7 @@ public final class AbyssalGatekeeper extends CardImpl {
this.toughness = new MageInt(1);
// When Abyssal Gatekeeper dies, each player sacrifices a creature.
this.addAbility(new DiesTriggeredAbility(new SacrificeAllEffect(1, new FilterControlledCreaturePermanent("creature"))));
this.addAbility(new DiesSourceTriggeredAbility(new SacrificeAllEffect(1, new FilterControlledCreaturePermanent("creature"))));
}
public AbyssalGatekeeper(final AbyssalGatekeeper card) {

Some files were not shown because too many files have changed in this diff Show more