Merge branch 'master' into additional-mulligan-support-5600

This commit is contained in:
John Hitchings 2019-03-19 23:39:39 -07:00
commit 47c9aab877
39 changed files with 546 additions and 320 deletions

View file

@ -56,7 +56,6 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
<dependency>
<groupId>org.swinglabs</groupId>

View file

@ -1,53 +1,46 @@
package mage.client.deck.generator;
import java.util.ArrayList;
import com.google.common.collect.ImmutableList;
import java.util.List;
public enum DeckGeneratorCMC {
Low(
new ArrayList<CMC>() {{
add(new CMC(0, 2, 0.60f));
add(new CMC(3, 4, 0.30f));
add(new CMC(5, 6, 0.10f));
}},
new ArrayList<CMC>() {{
add(new CMC(0, 2, 0.65f));
add(new CMC(3, 4, 0.30f));
add(new CMC(5, 5, 0.05f));
}}),
Default(
new ArrayList<CMC>() {{
add(new CMC(0, 2, 0.20f));
add(new CMC(3, 5, 0.50f));
add(new CMC(6, 7, 0.25f));
add(new CMC(8, 100, 0.05f));
}},
new ArrayList<CMC>() {{
add(new CMC(0, 2, 0.30f));
add(new CMC(3, 4, 0.45f));
add(new CMC(5, 6, 0.20f));
add(new CMC(7, 100, 0.05f));
}}),
High(
new ArrayList<CMC>() {{
add(new CMC(0, 2, 0.05f));
add(new CMC(3, 5, 0.35f));
add(new CMC(6, 7, 0.40f));
add(new CMC(8, 100, 0.15f));
}},
new ArrayList<CMC>() {{
add(new CMC(0, 2, 0.10f));
add(new CMC(3, 4, 0.30f));
add(new CMC(5, 6, 0.45f));
add(new CMC(7, 100, 0.15f));
}});
Low(ImmutableList.<CMC>builder()
.add(new CMC(0, 2, 0.60f))
.add(new CMC(3, 4, 0.30f))
.add(new CMC(5, 6, 0.10f)).build(),
ImmutableList.<CMC>builder()
.add(new CMC(0, 2, 0.65f))
.add(new CMC(3, 4, 0.30f))
.add(new CMC(5, 5, 0.05f)).build()),
Default(ImmutableList.<CMC>builder()
.add(new CMC(0, 2, 0.20f))
.add(new CMC(3, 5, 0.50f))
.add(new CMC(6, 7, 0.25f))
.add(new CMC(8, 100, 0.05f)).build(),
ImmutableList.<CMC>builder()
.add(new CMC(0, 2, 0.30f))
.add(new CMC(3, 4, 0.45f))
.add(new CMC(5, 6, 0.20f))
.add(new CMC(7, 100, 0.05f)).build()),
private final ArrayList<CMC> poolCMCs60;
private final ArrayList<CMC> poolCMCs40;
High(ImmutableList.<CMC>builder().
add(new CMC(0, 2, 0.05f))
.add(new CMC(3, 5, 0.35f))
.add(new CMC(6, 7, 0.40f))
.add(new CMC(8, 100, 0.15f)).build(),
ImmutableList.<CMC>builder().
add(new CMC(0, 2, 0.10f))
.add(new CMC(3, 4, 0.30f))
.add(new CMC(5, 6, 0.45f))
.add(new CMC(7, 100, 0.15f)).build());
DeckGeneratorCMC(ArrayList<CMC> CMCs60, ArrayList<CMC> CMCs40) {
private final List<CMC> poolCMCs60;
private final List<CMC> poolCMCs40;
DeckGeneratorCMC(List<CMC> CMCs60, List<CMC> CMCs40) {
this.poolCMCs60 = CMCs60;
this.poolCMCs40 = CMCs40;
}
@ -60,8 +53,7 @@ public enum DeckGeneratorCMC {
return this.poolCMCs60;
}
static class CMC
{
static class CMC {
public final int min;
public final int max;
public final float percentage;
@ -69,12 +61,12 @@ public enum DeckGeneratorCMC {
/**
* Constructs a CMC range given a minimum and maximum, and the percentage of cards that are in this range.
* @param min the minimum CMC a card in this range can be.
* @param max the maximum CMC a card in this range can be.
*
* @param min the minimum CMC a card in this range can be.
* @param max the maximum CMC a card in this range can be.
* @param percentage the percentage of cards in the range (min, max)
*/
CMC(int min, int max, float percentage)
{
CMC(int min, int max, float percentage) {
this.min = min;
this.max = max;
this.percentage = percentage;
@ -82,19 +74,19 @@ public enum DeckGeneratorCMC {
/**
* Sets the amount of cards needed in this CMC range.
*
* @param amount the number of cards needed.
*/
public void setAmount(int amount)
{
public void setAmount(int amount) {
this.amount = amount;
}
/**
* Gets the number of cards needed in this CMC range.
*
* @return the number of cards needed in this CMC range.
*/
public int getAmount()
{
public int getAmount() {
return this.amount;
}
}

View file

@ -11,7 +11,6 @@ import javax.swing.*;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import mage.cards.Sets;
import mage.cards.decks.Deck;
import mage.client.MageFrame;
import mage.client.dialog.PreferencesDialog;
@ -19,6 +18,8 @@ import mage.client.util.gui.ColorsChooser;
import mage.client.util.gui.FastSearchUtil;
import mage.client.util.sets.ConstructedFormats;
import static mage.cards.decks.DeckFormats.DCK;
/**
*
* @author Simown
@ -328,7 +329,7 @@ public class DeckGeneratorDialog {
tmp.getParentFile().mkdirs();
tmp.createNewFile();
deck.setName(deckName);
Sets.saveDeck(tmp.getAbsolutePath(), deck.getDeckCardLists());
DCK.getExporter().writeDeck(tmp.getAbsolutePath(), deck.getDeckCardLists());
cleanUp();
return tmp.getAbsolutePath();
} catch (Exception e) {

View file

@ -1,7 +1,6 @@
package mage.client.deckeditor;
import mage.cards.Card;
import mage.cards.Sets;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardLists;
import mage.cards.decks.DnDDeckTargetListener;
@ -35,12 +34,13 @@ import java.awt.*;
import java.awt.dnd.DropTarget;
import java.awt.event.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.*;
import java.util.concurrent.*;
import static mage.cards.decks.DeckFormats.DCK;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -965,8 +965,8 @@ public class DeckEditorPanel extends javax.swing.JPanel {
DeckCardLists cardLists = deck.getDeckCardLists();
cardLists.setCardLayout(deckArea.getCardLayout());
cardLists.setSideboardLayout(deckArea.getSideboardLayout());
Sets.saveDeck(fileName, cardLists);
} catch (FileNotFoundException ex) {
DCK.getExporter().writeDeck(fileName, cardLists);
} catch (IOException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage() + "\nTry ensuring that the selected directory is writable.", "Error saving deck", JOptionPane.ERROR_MESSAGE);
} finally {
MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));

View file

@ -1,22 +1,15 @@
/*
* ErrorDialog.java
*
* Created on Dec 23, 2009, 11:01:32 AM
*/
package mage.client.dialog;
import java.awt.Dimension;
import java.awt.Font;
import javax.swing.JComponent;
import javax.swing.plaf.basic.BasicInternalFrameUI;
import mage.client.MageFrame;
import mage.client.util.GUISizeHelper;
import mage.constants.PlayerAction;
import mage.view.UserRequestMessage;
import javax.swing.*;
import javax.swing.plaf.basic.BasicInternalFrameUI;
import java.awt.*;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class UserRequestDialog extends MageDialog {
@ -34,7 +27,7 @@ public class UserRequestDialog extends MageDialog {
private void setGUISize() {
Font font = GUISizeHelper.gameRequestsFont;
lblText.setFont(font);
lblText.setMaximumSize(new Dimension(300 + font.getSize() * 15, 20 + font.getSize() * 5));
lblText.setMaximumSize(new Dimension(300 + font.getSize() * 15, 200 + font.getSize() * 5));
lblText.setMinimumSize(new Dimension(300 + font.getSize() * 15, 20 + font.getSize() * 5));
lblText.setPreferredSize(new Dimension(300 + font.getSize() * 15, 20 + font.getSize() * 5));
btn1.setFont(font);
@ -58,7 +51,7 @@ public class UserRequestDialog extends MageDialog {
public void showDialog(UserRequestMessage userRequestMessage) {
this.userRequestMessage = userRequestMessage;
this.setTitle(userRequestMessage.getTitel());
this.setTitle(userRequestMessage.getTitle());
String text = "<html><p style=\"text-align:center; margin-left:10px; margin-right:10px\">" + userRequestMessage.getMessage() + "</p></html>";
this.lblText.setText(text);
if (userRequestMessage.getButton1Text() != null) {
@ -128,30 +121,30 @@ public class UserRequestDialog extends MageDialog {
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(lblText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(btn3, javax.swing.GroupLayout.PREFERRED_SIZE, 1, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btn2, javax.swing.GroupLayout.PREFERRED_SIZE, 1, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btn1, javax.swing.GroupLayout.PREFERRED_SIZE, 1, Short.MAX_VALUE)))
.addContainerGap())
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(lblText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(btn3, javax.swing.GroupLayout.PREFERRED_SIZE, 1, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btn2, javax.swing.GroupLayout.PREFERRED_SIZE, 1, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btn1, javax.swing.GroupLayout.PREFERRED_SIZE, 1, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(lblText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btn1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btn2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btn3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(12, 12, 12))
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(lblText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btn1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btn2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btn3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(12, 12, 12))
);
pack();

View file

@ -1,5 +1,6 @@
package org.mage.plugins.card.dl.sources;
import com.google.common.collect.ImmutableMap;
import org.tritonus.share.ArraySet;
import java.util.HashMap;
@ -12,22 +13,19 @@ import java.util.Set;
*/
public class ScryfallImageSupportCards {
private static final Map<String, String> xmageSetsToScryfall = new HashMap<String, String>() {
{
// xmage -> scryfall
put("DD3GVL", "gvl");
put("DD3JVC", "jvc");
put("DD3DVD", "dvd");
put("DD3EVG", "evg");
put("MPS-AKH", "mp2");
put("MBP", "pmei");
put("WMCQ", "pwcq");
put("EURO", "pelp");
put("GPX", "pgpx");
put("MED", "me1");
put("MEDM", "med");
}
};
private static final Map<String, String> xmageSetsToScryfall = ImmutableMap.<String, String>builder().put("DD3GVL", "gvl").
put("DD3JVC", "jvc").
put("DD3DVD", "dvd").
put("DD3EVG", "evg").
put("MPS-AKH", "mp2").
put("MBP", "pmei").
put("WMCQ", "pwcq").
put("EURO", "pelp").
put("GPX", "pgpx").
put("MED", "me1").
put("MEDM", "med").build();
private static final Set<String> supportedSets = new ArraySet<String>() {
{

View file

@ -89,7 +89,7 @@ public enum TokensMtgImageSource implements CardImageSource {
}
// Image URL contains token number
// e.g. http://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg -- token number 010
// e.g. https://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg -- token number 010
// We don't know these numbers, but we can take them from a file
// with tokens information that can be downloaded from the site.
if (tokensData.isEmpty()) {
@ -115,7 +115,7 @@ public enum TokensMtgImageSource implements CardImageSource {
tokenData = list.get(card.getType() - 1);
}
String url = "http://tokens.mtg.onl/tokens/" + tokenData.getExpansionSetCode().trim() + '_'
String url = "https://tokens.mtg.onl/tokens/" + tokenData.getExpansionSetCode().trim() + '_'
+ tokenData.getNumber().trim() + '-' + tokenData.getName().trim() + ".jpg";
url = url.replace(' ', '-');
return new CardImageUrls(url);

View file

@ -19,15 +19,15 @@ public class TokensMtgImageSourceTest {
CardImageSource imageSource = TokensMtgImageSource.instance;
CardImageUrls url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 1, "ORI", ""));
Assert.assertEquals("http://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg", url.baseUrl);
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 2, "ORI", ""));
Assert.assertEquals("http://tokens.mtg.onl/tokens/ORI_011-Thopter.jpg", url.baseUrl);
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_011-Thopter.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Ashaya, the Awoken World", "ORI", "0", false, 0, "ORI", ""));
Assert.assertEquals("http://tokens.mtg.onl/tokens/ORI_007-Ashaya,-the-Awoken-World.jpg", url.baseUrl);
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_007-Ashaya,-the-Awoken-World.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Emblem Gideon, Ally of Zendikar", "BFZ", "0", false, 0, null, ""));
Assert.assertEquals("http://tokens.mtg.onl/tokens/BFZ_012-Gideon-Emblem.jpg", url.baseUrl);
Assert.assertEquals("https://tokens.mtg.onl/tokens/BFZ_012-Gideon-Emblem.jpg", url.baseUrl);
}
}

View file

@ -1,11 +1,9 @@
package mage.remote;
import mage.MageException;
import mage.utils.MageVersion;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class MageVersionException extends MageException {
@ -13,7 +11,11 @@ public class MageVersionException extends MageException {
private final MageVersion serverVersion;
public MageVersionException(MageVersion clientVersion, MageVersion serverVersion) {
super("Wrong client version " + clientVersion + ", expecting version " + serverVersion + ". \r\n\r\nPlease download needed version from http://XMage.de or http://www.slightlymagic.net/forum/viewforum.php?f=70");
super("Wrong client version."
+ "<br/>Your version: " + clientVersion
+ "<br/>Server version: " + serverVersion
+ "<br/>Release app download: http://xmage.de"
+ "<br/>BETA app download: http://xmage.today");
this.serverVersion = serverVersion;
}

View file

@ -1,19 +1,18 @@
package mage.view;
import mage.constants.PlayerAction;
import java.io.Serializable;
import java.util.UUID;
import mage.constants.PlayerAction;
/**
*
* @author LevelX2
*/
public class UserRequestMessage implements Serializable {
private static final long serialVersionUID = 1L;
private final String titel;
private final String title;
private final String message;
private UUID relatedUserId;
private String relatedUserName;
@ -32,8 +31,8 @@ public class UserRequestMessage implements Serializable {
private String button3Text;
private PlayerAction button3Action;
public UserRequestMessage(String titel, String message) {
this.titel = titel;
public UserRequestMessage(String title, String message) {
this.title = title;
this.message = message;
this.button1Action = null;
this.button2Action = null;
@ -68,8 +67,8 @@ public class UserRequestMessage implements Serializable {
this.button3Action = buttonAction;
}
public String getTitel() {
return titel;
public String getTitle() {
return title;
}
public static long getSerialVersionUID() {

View file

@ -15,6 +15,7 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.TargetController;
import mage.filter.StaticFilters;
import mage.game.Game;
@ -34,6 +35,7 @@ public final class AurraSingBaneOfJedi extends CardImpl {
public AurraSingBaneOfJedi(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{B}{R}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AURRA);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3));

View file

@ -9,6 +9,7 @@ import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.keyword.MenaceAbility;
import mage.abilities.keyword.MonstrosityAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
@ -28,6 +29,9 @@ public final class BullRancor extends CardImpl {
this.power = new MageInt(7);
this.toughness = new MageInt(7);
// Trample
this.addAbility(TrampleAbility.getInstance());
// {3}{R}{G}{G}{W}: Monstrosity 3.
this.addAbility(new MonstrosityAbility("{3}{R}{G}{G}{W}", 3));

View file

@ -14,6 +14,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Duration;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
@ -36,6 +37,7 @@ public final class DarthSidiousSithLord extends CardImpl {
public DarthSidiousSithLord(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{4}{U}{B}{B}{R}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.SIDIOUS);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5));

View file

@ -14,6 +14,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
@ -35,6 +36,7 @@ public final class DarthTyranusCountOfSerenno extends CardImpl {
public DarthTyranusCountOfSerenno(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{1}{W}{U}{B}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.DOOKU);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3));

View file

@ -91,7 +91,7 @@ class GrimFeastEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Permanent creature = game.getPermanentOrLKIBattlefield(source.getFirstTarget());
Permanent creature = game.getPermanentOrLKIBattlefield(targetPointer.getFirst(game, source));
if (creature == null) {
return false;
}

View file

@ -1,37 +1,30 @@
package mage.cards.j;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.abilities.keyword.HasteAbility;
import mage.abilities.keyword.MenaceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.filter.predicate.permanent.CounterPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.common.TargetOpponentsCreaturePermanent;
import java.util.UUID;
/**
*
* @author Styxo/spjspj
*/
public final class JangoFett extends CardImpl {
@ -55,7 +48,7 @@ public final class JangoFett extends CardImpl {
ability.addTarget(new TargetOpponentsCreaturePermanent());
this.addAbility(ability);
// Whenever Jango Fett attacks, it deals X damage to defending player and target creature he or she controls, where X is the number of creatures defending player controls with a bounty counter on them.
// Whenever Jango Fett attacks, it gets +X/+0, where X is the number of creatures defending player controls with a bounty counter on them
this.addAbility(new JangoFettTriggeredAbility(new JangoFettEffect(), false));
}
@ -97,11 +90,6 @@ class JangoFettTriggeredAbility extends TriggeredAbilityImpl {
if (event.getSourceId().equals(this.getSourceId())) {
UUID defenderId = game.getCombat().getDefendingPlayerId(getSourceId(), game);
if (defenderId != null) {
this.getTargets().clear();
FilterCreaturePermanent filter = new FilterCreaturePermanent("target creature defending player controls");
filter.add(new ControllerIdPredicate(defenderId));
TargetPermanent target = new TargetPermanent(filter);
this.addTarget(target);
return true;
}
}
@ -125,8 +113,8 @@ class JangoFettTriggeredAbility extends TriggeredAbilityImpl {
class JangoFettEffect extends OneShotEffect {
public JangoFettEffect() {
super(Outcome.Damage);
this.staticText = "it deals X damage to defending player and target creature he or she controls, where X is the number of creatures defending player controls with a bounty counter on them";
super(Outcome.BoostCreature);
this.staticText = "it gets +X/+0, where X is the number of creatures defending player controls with a bounty counter on them";
}
public JangoFettEffect(final JangoFettEffect ability) {
@ -158,14 +146,7 @@ class JangoFettEffect extends OneShotEffect {
return false;
}
Permanent targetCreature = game.getPermanent(source.getFirstTarget());
if (targetCreature != null) {
targetCreature.damage(count, source.getSourceId(), game, false, true);
}
Player defender = game.getPlayer(defenderId);
defender.damage(count, source.getSourceId(), game, false, true);
game.addEffect(new BoostSourceEffect(count, 0, Duration.WhileOnBattlefield), source);
return true;
}
}

View file

@ -47,7 +47,7 @@ public final class KrovikanVampire extends CardImpl {
Zone.BATTLEFIELD,
new KrovikanVampireEffect(),
TargetController.ANY,
KrovikanVampireInterveningIfCondition.instance,
new KrovikanVampireInterveningIfCondition(),
false);
ability.addWatcher(new KrovikanVampireCreaturesDamagedWatcher());
ability.addWatcher(new KrovikanVampireCreaturesDiedWatcher());
@ -85,14 +85,17 @@ class KrovikanVampireEffect extends OneShotEffect {
if (creaturesAffected != null
&& controller != null
&& krovikanVampire != null) {
for (UUID creatureId : creaturesAffected) {
creaturesAffected.stream().map((creatureId) -> {
controller.moveCards(game.getCard(creatureId), Zone.BATTLEFIELD, source, game, false, false, false, null);
return creatureId;
}).map((creatureId) -> {
OneShotEffect effect = new SacrificeTargetEffect();
effect.setText("Sacrifice this if Krovikan Vampire leaves the battlefield or its current controller loses control of it.");
effect.setTargetPointer(new FixedTarget(creatureId));
KrovikanVampireDelayedTriggeredAbility dTA = new KrovikanVampireDelayedTriggeredAbility(effect, krovikanVampire.getId());
return effect;
}).map((effect) -> new KrovikanVampireDelayedTriggeredAbility(effect, krovikanVampire.getId())).forEachOrdered((dTA) -> {
game.addDelayedTriggeredAbility(dTA, source);
}
});
creaturesAffected.clear();
return true;
}
@ -105,9 +108,8 @@ class KrovikanVampireEffect extends OneShotEffect {
}
}
enum KrovikanVampireInterveningIfCondition implements Condition {
class KrovikanVampireInterveningIfCondition implements Condition {
instance;
Set<UUID> creaturesAffected = new HashSet<>();
@Override
@ -116,16 +118,12 @@ enum KrovikanVampireInterveningIfCondition implements Condition {
KrovikanVampireCreaturesDamagedWatcher watcherDamaged = game.getState().getWatcher(KrovikanVampireCreaturesDamagedWatcher.class);
if (watcherDied != null) {
Set<UUID> creaturesThatDiedThisTurn = watcherDied.getDiedThisTurn();
for (UUID mor : creaturesThatDiedThisTurn) {
if (watcherDamaged != null) {
for (UUID mor2 : watcherDamaged.getDamagedBySource()) {
if (mor2 != null
&& mor == mor2) {
creaturesAffected.add(mor);
}
}
}
}
creaturesThatDiedThisTurn.stream().filter((mor) -> (watcherDamaged != null)).forEachOrdered((mor) -> {
watcherDamaged.getDamagedBySource().stream().filter((mor2) -> (mor2 != null
&& mor == mor2)).forEachOrdered((_item) -> {
creaturesAffected.add(mor);
});
});
if (creaturesAffected != null
&& creaturesAffected.size() > 0) {
game.getState().setValue(source.getSourceId() + "creatureToGainControl", creaturesAffected);

View file

@ -1,4 +1,3 @@
package mage.cards.m;
import java.util.UUID;
@ -11,15 +10,14 @@ import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.common.FilterNonlandCard;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetOpponent;
@ -32,7 +30,7 @@ import mage.util.CardUtil;
public final class MesmericFiend extends CardImpl {
public MesmericFiend(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
this.subtype.add(SubType.NIGHTMARE);
this.subtype.add(SubType.HORROR);
@ -79,14 +77,16 @@ class MesmericFiendExileEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
Player opponent = game.getPlayer(source.getFirstTarget());
Permanent sourcePermanent = (Permanent) source.getSourceObject(game);
if (controller != null && opponent != null && sourcePermanent != null) {
if (controller != null
&& opponent != null
&& sourcePermanent != null) {
opponent.revealCards(sourcePermanent.getName(), opponent.getHand(), game);
TargetCard target = new TargetCard(Zone.HAND, new FilterNonlandCard("nonland card to exile"));
if (controller.choose(Outcome.Exile, opponent.getHand(), target, game)) {
Card card = opponent.getHand().get(target.getFirstTarget(), game);
if (card != null) {
controller.moveCardToExileWithInfo(card, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.HAND, true);
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
controller.moveCardsToExile(card, source, game, true, exileId, sourcePermanent.getName());
}
}
@ -117,11 +117,14 @@ class MesmericFiendLeaveEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) {
int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() - 1;
ExileZone exZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter));
if (exZone != null) {
return controller.moveCards(exZone, Zone.HAND, source, game);
if (controller != null
&& sourceObject != null) {
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() - 1);
if (exileId != null) {
Cards cards = game.getExile().getExileZone(exileId);
if (!cards.isEmpty()) {
return controller.moveCards(cards, Zone.HAND, source, game);
}
}
}
return false;

View file

@ -15,6 +15,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Duration;
import mage.game.command.emblems.ObiWanKenobiEmblem;
import mage.target.common.TargetCreaturePermanent;
@ -27,6 +28,7 @@ public final class ObiWanKenobi extends CardImpl {
public ObiWanKenobi(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{W}{U}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.OBI_WAN);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5));

View file

@ -1,4 +1,3 @@
package mage.cards.p;
import java.util.Optional;
@ -9,6 +8,7 @@ import mage.abilities.common.AsEntersBattlefieldAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.abilities.effects.common.ChooseACardNameEffect;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
@ -71,9 +71,10 @@ class PithingNeedleEffect extends ContinuousRuleModifyingEffectImpl {
public boolean applies(GameEvent event, Ability source, Game game) {
MageObject object = game.getObject(event.getSourceId());
Optional<Ability> ability = game.getAbility(event.getTargetId(), event.getSourceId());
if (ability.isPresent() && object != null) {
if (ability.isPresent()
&& object != null) {
if (game.getState().getPlayersInRange(source.getControllerId(), game).contains(event.getPlayerId()) // controller in range
&& ability.get().getAbilityType() != AbilityType.MANA
&& !(ability.get() instanceof ActivatedManaAbilityImpl) // not an activated mana ability
&& object.getName().equals(game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY))) {
return true;
}

View file

@ -1,10 +1,8 @@
package mage.cards.p;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeSourceCost;
@ -17,6 +15,7 @@ import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
@ -27,12 +26,19 @@ import mage.util.CardUtil;
public final class PyxisOfPandemonium extends CardImpl {
public PyxisOfPandemonium(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}");
// {T}: Each player exiles the top card of their library face down.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new PyxisOfPandemoniumExileEffect(), new TapSourceCost()));
this.addAbility(new SimpleActivatedAbility(
Zone.BATTLEFIELD,
new PyxisOfPandemoniumExileEffect(),
new TapSourceCost()));
// {7}, {T}, Sacrifice Pyxis of Pandemonium: Each player turns face up all cards he or she owns exiled with Pyxis of Pandemonium, then puts all permanent cards among them onto the battlefield.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PyxisOfPandemoniumPutOntoBattlefieldEffect(), new GenericManaCost(7));
Ability ability = new SimpleActivatedAbility(
Zone.BATTLEFIELD,
new PyxisOfPandemoniumPutOntoBattlefieldEffect(),
new GenericManaCost(7));
ability.addCost(new TapSourceCost());
ability.addCost(new SacrificeSourceCost());
this.addAbility(ability);
@ -68,10 +74,11 @@ class PyxisOfPandemoniumExileEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (sourceObject != null && controller != null) {
Permanent pyxis = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (pyxis != null
&& controller != null) {
Map<String, UUID> exileIds;
String valueKey = CardUtil.getObjectZoneString("exileIds", sourceObject, game);
String valueKey = CardUtil.getObjectZoneString("exileIds", pyxis, game);
Object object = game.getState().getValue(valueKey);
if (object instanceof Map) {
exileIds = (Map<String, UUID>) object;
@ -79,20 +86,22 @@ class PyxisOfPandemoniumExileEffect extends OneShotEffect {
exileIds = new HashMap<>();
game.getState().setValue(valueKey, exileIds);
}
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
game.getState().getPlayersInRange(controller.getId(), game).forEach((playerId) -> {
Player player = game.getPlayer(playerId);
if (player != null) {
if (player.getLibrary().hasCards()) {
Card card = player.getLibrary().getFromTop(game);
String exileKey = playerId.toString() + CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()).toString();
String exileKey = playerId.toString()
+ CardUtil.getExileZoneId(game,
source.getSourceId(),
pyxis.getZoneChangeCounter(game));
UUID exileId = exileIds.computeIfAbsent(exileKey, k -> UUID.randomUUID());
player.moveCardsToExile(card, source, game, false, exileId, sourceObject.getIdName() + " (" + player.getName() + ')');
player.moveCardsToExile(card, source, game, false,
exileId, pyxis.getIdName() + " (" + player.getName() + ')');
card.setFaceDown(true, game);
}
}
}
});
return true;
}
return false;
@ -103,7 +112,8 @@ class PyxisOfPandemoniumPutOntoBattlefieldEffect extends OneShotEffect {
public PyxisOfPandemoniumPutOntoBattlefieldEffect() {
super(Outcome.PutCardInPlay);
this.staticText = "Each player turns face up all cards he or she owns exiled with {this}, then puts all permanent cards among them onto the battlefield";
this.staticText = "Each player turns face up all cards he or she owns exiled with {this}, "
+ "then puts all permanent cards among them onto the battlefield";
}
public PyxisOfPandemoniumPutOntoBattlefieldEffect(final PyxisOfPandemoniumPutOntoBattlefieldEffect effect) {
@ -118,10 +128,11 @@ class PyxisOfPandemoniumPutOntoBattlefieldEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) {
Permanent pyxis = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (controller != null
&& pyxis != null) {
Map<String, UUID> exileIds;
String valueKey = CardUtil.getObjectZoneString("exileIds", sourceObject, game);
String valueKey = CardUtil.getObjectZoneString("exileIds", pyxis, game);
Object object = game.getState().getValue(valueKey);
if (object instanceof Map) {
exileIds = (Map<String, UUID>) object;
@ -129,24 +140,26 @@ class PyxisOfPandemoniumPutOntoBattlefieldEffect extends OneShotEffect {
return true;
}
Cards cardsToBringIntoPlay = new CardsImpl();
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
game.getState().getPlayersInRange(controller.getId(), game).forEach((playerId) -> {
Player player = game.getPlayer(playerId);
if (player != null) {
String exileKey = playerId.toString() + CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()).toString();
String exileKey = playerId.toString() + CardUtil.getExileZoneId(game,
source.getSourceId(),
pyxis.getZoneChangeCounter(game));
UUID exileId = exileIds.get(exileKey);
if (exileId != null) {
ExileZone exileZone = game.getState().getExile().getExileZone(exileId);
if (exileZone != null) {
for (Card card : exileZone.getCards(game)) {
exileZone.getCards(game).stream().map((card) -> {
card.setFaceDown(false, game);
if (card.isPermanent()) {
cardsToBringIntoPlay.add(card);
}
}
return card;
}).filter((card) -> (card.isPermanent())).forEachOrdered((card) -> {
cardsToBringIntoPlay.add(card);
});
}
}
}
}
});
controller.moveCards(cardsToBringIntoPlay.getCards(game), Zone.BATTLEFIELD, source, game, false, false, true, null);
return true;
}

View file

@ -20,7 +20,7 @@ import mage.constants.SubType;
*/
public final class ScreechingSilcaw extends CardImpl {
private static final String text = "<i>Metalcraft</i> &mdash; Whenever Screeching Silcaw deals combat damage to a player, if you control three or more artifacts, that player puts the top four cards of their library into their graveyard.";
private static final String rule = "<i>Metalcraft</i> &mdash; Whenever Screeching Silcaw deals combat damage to a player, if you control three or more artifacts, that player puts the top four cards of their library into their graveyard.";
public ScreechingSilcaw(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}");
@ -34,7 +34,7 @@ public final class ScreechingSilcaw extends CardImpl {
//"<i>Metalcraft</i> &mdash; Whenever Screeching Silcaw deals combat damage to a player, if you control three or more artifacts, that player puts the top four cards of their library into their graveyard.
TriggeredAbility conditional = new ConditionalInterveningIfTriggeredAbility(
new DealsCombatDamageToAPlayerTriggeredAbility(new PutLibraryIntoGraveTargetEffect(4), false, true),
MetalcraftCondition.instance, text);
MetalcraftCondition.instance, rule);
this.addAbility(conditional);
}

View file

@ -16,6 +16,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
@ -44,6 +45,7 @@ public final class YodaJediMaster extends CardImpl {
public YodaJediMaster(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{G}{U}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.YODA);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3));

View file

@ -383,6 +383,19 @@ public class VerifyCardDataTest {
}
}
// 2. all planeswalkers must be legendary
for (ExpansionSet set : sets) {
for (ExpansionSet.SetCardInfo cardInfo : set.getSetCardInfo()) {
Card card = CardImpl.createCard(cardInfo.getCardClass(), new CardSetInfo(cardInfo.getName(), set.getCode(),
cardInfo.getCardNumber(), cardInfo.getRarity(), cardInfo.getGraphicInfo()));
Assert.assertNotNull(card);
if (card.getCardType().contains(CardType.PLANESWALKER) && !card.getSuperType().contains(SuperType.LEGENDARY)) {
errorsList.add("error, planeswalker must have legendary type: " + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber());
}
}
}
printMessages(warningsList);
printMessages(errorsList);
if (errorsList.size() > 0) {

View file

@ -26,6 +26,10 @@
<version>1.4.197</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.j256.ormlite</groupId>
<artifactId>ormlite-jdbc</artifactId>

View file

@ -325,15 +325,9 @@ public abstract class AbilityImpl implements Ability {
}
if (!getTargets().isEmpty()) {
Outcome outcome = getEffects().isEmpty() ? Outcome.Detriment : getEffects().get(0).getOutcome();
// can be cancel by user
if (getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game, true) == false) {
/*
if ((variableManaCost != null) || (announceString != null && !announceString.isEmpty())) {
// ?debug message?
game.informPlayer(controller, (sourceObject != null ? sourceObject.getIdName() : "") + ": no valid targets");
}
*/
// when activation of ability is canceled during target selection
// only activated abilities can be canceled by user (not triggered)
if (!getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game, this instanceof ActivatedAbility)) {
// was canceled during targer selection
return false;
}
}

View file

@ -48,7 +48,8 @@ public class CanAttackAsThoughItDidntHaveDefenderAllEffect extends AsThoughEffec
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
Permanent permanent = game.getPermanent(objectId);
return permanent != null && filter.match(permanent, source.getSourceId(), affectedControllerId, game);
return permanent != null
&& filter.match(permanent, source.getSourceId(), source.getControllerId(), game);
}
private String getText() {

View file

@ -1,5 +1,6 @@
package mage.cards;
import com.google.common.collect.ImmutableList;
import mage.MageObject;
import mage.MageObjectImpl;
import mage.Mana;
@ -224,11 +225,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
game.getState().getCardState(objectId).addInfo(key, value);
}
protected static final ArrayList<String> rulesError = new ArrayList<String>() {
{
add("Exception occurred in rules generation");
}
};
protected static final List<String> rulesError = ImmutableList.of("Exception occurred in rules generation");
@Override
public List<String> getRules() {

View file

@ -1,9 +1,6 @@
package mage.cards;
import mage.Mana;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLayout;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
@ -16,8 +13,6 @@ import mage.util.RandomUtil;
import org.apache.log4j.Logger;
import org.junit.Assert;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.*;
/**
@ -205,73 +200,4 @@ public class Sets extends HashMap<String, ExpansionSet> {
return null;
}
public static void saveDeck(String file, DeckCardLists deck) throws FileNotFoundException {
Map<String, DeckCardInfo> deckCards = new HashMap<>();
Map<String, DeckCardInfo> sideboard = new HashMap<>();
try (PrintWriter out = new PrintWriter(file)) {
if (deck.getName() != null && !deck.getName().isEmpty()) {
out.println("NAME:" + deck.getName());
}
if (deck.getAuthor() != null && !deck.getAuthor().isEmpty()) {
out.println("AUTHOR:" + deck.getAuthor());
}
for (DeckCardInfo deckCardInfo : deck.getCards()) {
if (deckCards.containsKey(deckCardInfo.getCardKey())) {
deckCards.put(deckCardInfo.getCardKey(), deckCards.get(deckCardInfo.getCardKey()).increaseQuantity());
} else {
deckCards.put(deckCardInfo.getCardKey(), deckCardInfo);
}
}
for (DeckCardInfo deckCardInfo : deck.getSideboard()) {
if (sideboard.containsKey(deckCardInfo.getCardKey())) {
sideboard.put(deckCardInfo.getCardKey(), sideboard.get(deckCardInfo.getCardKey()).increaseQuantity());
} else {
sideboard.put(deckCardInfo.getCardKey(), deckCardInfo);
}
}
// Write out all of the cards
for (Entry<String, DeckCardInfo> entry : deckCards.entrySet()) {
out.printf("%d [%s:%s] %s%n", entry.getValue().getQuantity(), entry.getValue().getSetCode(), entry.getValue().getCardNum(), entry.getValue().getCardName());
}
for (Entry<String, DeckCardInfo> entry : sideboard.entrySet()) {
out.printf("SB: %d [%s:%s] %s%n", entry.getValue().getQuantity(), entry.getValue().getSetCode(), entry.getValue().getCardNum(), entry.getValue().getCardName());
}
// Write out the layout
out.print("LAYOUT MAIN:");
writeCardLayout(out, deck.getCardLayout());
out.print("\n");
out.print("LAYOUT SIDEBOARD:");
writeCardLayout(out, deck.getSideboardLayout());
out.print("\n");
}
}
private static void writeCardLayout(PrintWriter out, DeckCardLayout layout) {
if (layout == null) {
return;
}
List<List<List<DeckCardInfo>>> cardGrid = layout.getCards();
int height = cardGrid.size();
int width = (height > 0) ? cardGrid.get(0).size() : 0;
out.print("(" + height + ',' + width + ')');
out.print(layout.getSettings());
out.print("|");
for (List<List<DeckCardInfo>> row : cardGrid) {
for (List<DeckCardInfo> stack : row) {
out.print("(");
for (int i = 0; i < stack.size(); ++i) {
DeckCardInfo info = stack.get(i);
out.printf("[%s:%s]", info.getSetCode(), info.getCardNum());
if (i != stack.size() - 1) {
out.print(",");
}
}
out.print(")");
}
}
}
}

View file

@ -0,0 +1,90 @@
package mage.cards.decks;
import mage.cards.decks.exporter.DckExporter;
import mage.cards.decks.exporter.DeckExporter;
import mage.cards.decks.exporter.MtgoExporter;
import java.io.*;
import java.util.Optional;
public enum DeckFormats {
DCK(new DckExporter()),
MTGO(new MtgoExporter());
private final DeckExporter exporter;
DeckFormats(DeckExporter exporter) {
this.exporter = exporter;
}
public DeckExporter getExporter() {
return exporter;
}
public static Optional<DeckFormats> getFormatForExtension(String filename) {
return getExtension(filename).map(c -> {
try {
return DeckFormats.valueOf(c);
} catch (IllegalArgumentException e) {
return null;
}
});
}
public static Optional<String> getExtension(String filename) {
int i = filename.lastIndexOf('.');
if (i > 0) {
return Optional.of(filename.substring(i+1).toUpperCase());
} else {
return Optional.empty();
}
}
public static void writeDeck(String file, DeckCardLists deck) throws IOException {
writeDeck(new File(file), deck);
}
public static void writeDeck(String file, DeckCardLists deck, DeckFormats format) throws IOException {
writeDeck(new File(file), deck, format);
}
public static void writeDeck(String file, DeckCardLists deck, DeckExporter exporter) throws IOException {
writeDeck(new File(file), deck, exporter);
}
public static void writeDeck(File file, DeckCardLists deck) throws IOException {
DeckFormats format = DeckFormats.getFormatForExtension(file.getName()).orElseGet(() -> {
throw new IllegalArgumentException("Could not determine deck export format.");
});
writeDeck(file, deck, format);
}
public static void writeDeck(File file, DeckCardLists deck, DeckFormats format) throws IOException {
writeDeck(file, deck, format.getExporter());
}
public static void writeDeck(File file, DeckCardLists deck, DeckExporter exporter) throws IOException {
try (FileOutputStream out = new FileOutputStream(file)){
writeDeck(out, deck, exporter);
}
}
public static void writeDeck(OutputStream out, DeckCardLists deck, DeckFormats format) {
writeDeck(new PrintWriter(out), deck, format);
}
public static void writeDeck(OutputStream out, DeckCardLists deck, DeckExporter exporter) {
writeDeck(new PrintWriter(out), deck, exporter);
}
public static void writeDeck(PrintWriter out, DeckCardLists deck, DeckFormats format) {
writeDeck(out, deck, format.getExporter());
}
public static void writeDeck(PrintWriter out, DeckCardLists deck, DeckExporter exporter) {
exporter.writeDeck(out, deck);
out.flush();
}
}

View file

@ -0,0 +1,82 @@
package mage.cards.decks.exporter;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLayout;
import mage.cards.decks.DeckCardLists;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DckExporter extends DeckExporter {
public void writeDeck(PrintWriter out, DeckCardLists deck) {
Map<String, DeckCardInfo> deckCards = new HashMap<>();
Map<String, DeckCardInfo> sideboard = new HashMap<>();
if (deck.getName() != null && !deck.getName().isEmpty()) {
out.println("NAME:" + deck.getName());
}
if (deck.getAuthor() != null && !deck.getAuthor().isEmpty()) {
out.println("AUTHOR:" + deck.getAuthor());
}
for (DeckCardInfo deckCardInfo : deck.getCards()) {
if (deckCards.containsKey(deckCardInfo.getCardKey())) {
deckCards.put(deckCardInfo.getCardKey(), deckCards.get(deckCardInfo.getCardKey()).increaseQuantity());
} else {
deckCards.put(deckCardInfo.getCardKey(), deckCardInfo);
}
}
for (DeckCardInfo deckCardInfo : deck.getSideboard()) {
if (sideboard.containsKey(deckCardInfo.getCardKey())) {
sideboard.put(deckCardInfo.getCardKey(), sideboard.get(deckCardInfo.getCardKey()).increaseQuantity());
} else {
sideboard.put(deckCardInfo.getCardKey(), deckCardInfo);
}
}
// Write out all of the cards
for (Map.Entry<String, DeckCardInfo> entry : deckCards.entrySet()) {
out.printf("%d [%s:%s] %s%n", entry.getValue().getQuantity(), entry.getValue().getSetCode(), entry.getValue().getCardNum(), entry.getValue().getCardName());
}
for (Map.Entry<String, DeckCardInfo> entry : sideboard.entrySet()) {
out.printf("SB: %d [%s:%s] %s%n", entry.getValue().getQuantity(), entry.getValue().getSetCode(), entry.getValue().getCardNum(), entry.getValue().getCardName());
}
// Write out the layout
out.print("LAYOUT MAIN:");
writeCardLayout(out, deck.getCardLayout());
out.println("");
out.print("LAYOUT SIDEBOARD:");
writeCardLayout(out, deck.getSideboardLayout());
out.println("");
}
private static void writeCardLayout(PrintWriter out, DeckCardLayout layout) {
if (layout == null) {
return;
}
List<List<List<DeckCardInfo>>> cardGrid = layout.getCards();
int height = cardGrid.size();
int width = (height > 0) ? cardGrid.get(0).size() : 0;
out.print("(" + height + ',' + width + ')');
out.print(layout.getSettings());
out.print("|");
for (List<List<DeckCardInfo>> row : cardGrid) {
for (List<DeckCardInfo> stack : row) {
out.print("(");
for (int i = 0; i < stack.size(); ++i) {
DeckCardInfo info = stack.get(i);
out.printf("[%s:%s]", info.getSetCode(), info.getCardNum());
if (i != stack.size() - 1) {
out.print(",");
}
}
out.print(")");
}
}
}
}

View file

@ -0,0 +1,24 @@
package mage.cards.decks.exporter;
import mage.cards.decks.DeckCardLists;
import mage.cards.decks.DeckFormats;
import java.io.*;
public abstract class DeckExporter {
public void writeDeck(String file, DeckCardLists deck) throws IOException {
DeckFormats.writeDeck(file, deck, this);
}
public void writeDeck(File file, DeckCardLists deck) throws IOException {
DeckFormats.writeDeck(file, deck, this);
}
public void writeDeck(OutputStream out, DeckCardLists deck) {
DeckFormats.writeDeck(out, deck, this);
}
public abstract void writeDeck(PrintWriter out, DeckCardLists deck);
}

View file

@ -0,0 +1,43 @@
package mage.cards.decks.exporter;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import java.io.PrintWriter;
import java.util.List;
import java.util.TreeMap;
public class MtgoExporter extends DeckExporter {
@Override
public void writeDeck(PrintWriter out, DeckCardLists deck) {
TreeMap<String, Integer> deckCards = toCardMap(deck.getCards());
TreeMap<String, Integer> sideboard = toCardMap(deck.getSideboard());
deckCards.forEach((name, count) -> {
out.print(count);
out.print(' ');
out.println(name);
});
out.println();
out.println();
sideboard.forEach((name, count) -> {
out.print(count);
out.print(' ');
out.println(name);
});
out.println();
}
private TreeMap<String, Integer> toCardMap(List<DeckCardInfo> cards) {
TreeMap<String, Integer> counts = new TreeMap<>();
for (DeckCardInfo card : cards) {
int count = counts.getOrDefault(card.getCardName(), 0) + card.getQuantity();
counts.put(card.getCardName(), count);
}
return counts;
}
}

View file

@ -116,9 +116,6 @@ public abstract class GameImpl implements Game, Serializable {
protected GameOptions gameOptions;
protected String startMessage;
public static volatile int copyCount = 0;
public static volatile long copyTime = 0;
// private final transient LinkedList<MageAction> actions;
private Player scorePlayer;
// private int score = 0;
@ -156,10 +153,6 @@ public abstract class GameImpl implements Game, Serializable {
}
public GameImpl(final GameImpl game) {
long t1 = 0;
if (logger.isDebugEnabled()) {
t1 = System.currentTimeMillis();
}
this.id = game.id;
this.ready = game.ready;
this.startingPlayerId = game.startingPlayerId;
@ -175,10 +168,7 @@ public abstract class GameImpl implements Game, Serializable {
this.lkiExtended.putAll(game.lkiExtended);
this.shortLivingLKI.putAll(game.shortLivingLKI);
this.permanentsEntering.putAll(game.permanentsEntering);
if (logger.isDebugEnabled()) {
copyCount++;
copyTime += (System.currentTimeMillis() - t1);
}
this.stateCheckRequired = game.stateCheckRequired;
this.scorePlayer = game.scorePlayer;
this.scopeRelevant = game.scopeRelevant;

View file

@ -24,8 +24,8 @@ public final class GarrukApexPredatorEmblem extends Emblem {
public GarrukApexPredatorEmblem() {
setName("Emblem Garruk");
Effect effect = new BoostTargetEffect(-1, 0, Duration.EndOfTurn);
effect.setText("it gets -1/-0");
Effect effect = new BoostTargetEffect(5, 5, Duration.EndOfTurn);
effect.setText("it gets +5/+5");
Ability ability = new AttackedByCreatureTriggeredAbility(Zone.COMMAND, effect, false, SetTargetPointer.PERMANENT);
effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn);
ability.addEffect(effect.concatBy("and"));

View file

@ -1,5 +1,6 @@
package mage.players;
import com.google.common.collect.ImmutableMap;
import mage.ConditionalMana;
import mage.MageObject;
import mage.MageObjectReference;
@ -178,11 +179,10 @@ public abstract class PlayerImpl implements Player, Serializable {
/**
* During some steps we can't play anything
*/
protected final Map<PhaseStep, Step.StepPart> silentPhaseSteps = new EnumMap<PhaseStep, Step.StepPart>(PhaseStep.class) {
{
put(PhaseStep.DECLARE_ATTACKERS, Step.StepPart.PRE);
}
};
protected final Map<PhaseStep, Step.StepPart> silentPhaseSteps = ImmutableMap.<PhaseStep, Step.StepPart>builder().
put(PhaseStep.DECLARE_ATTACKERS, Step.StepPart.PRE).build();
public PlayerImpl(String name, RangeOfInfluence range) {
this(UUID.randomUUID());

View file

@ -0,0 +1,31 @@
package mage.cards.decks.exporter;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
public class DckExporterTest {
@Test
public void writeDeck() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DeckCardLists deck = new DeckCardLists();
deck.getCards().add(new DeckCardInfo("Forest", "RNA", "1", 2));
deck.getCards().add(new DeckCardInfo("Plains", "RNA", "2", 3));
deck.getSideboard().add(new DeckCardInfo("Island", "RNA", "3", 2));
DckExporter exporter = new DckExporter();
exporter.writeDeck(baos, deck);
assertEquals("2 [1:RNA] Forest" + System.lineSeparator() +
"3 [2:RNA] Plains" + System.lineSeparator() +
"SB: 2 [3:RNA] Island" + System.lineSeparator() +
"LAYOUT MAIN:" + System.lineSeparator() +
"LAYOUT SIDEBOARD:" + System.lineSeparator(),
baos.toString());
}
}

View file

@ -0,0 +1,32 @@
package mage.cards.decks.exporter;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
public class MtgoExporterTest {
@Test
public void writeDeck() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DeckCardLists deck = new DeckCardLists();
deck.getCards().add(new DeckCardInfo("Forest", "RNA", "1", 2));
deck.getCards().add(new DeckCardInfo("Plains", "RNA", "2", 3));
deck.getSideboard().add(new DeckCardInfo("Island", "RNA", "3", 2));
MtgoExporter exporter = new MtgoExporter();
exporter.writeDeck(baos, deck);
assertEquals("2 Forest" + System.lineSeparator() +
"3 Plains" + System.lineSeparator() +
System.lineSeparator() +
System.lineSeparator() +
"2 Island" + System.lineSeparator() +
System.lineSeparator(),
baos.toString());
}
}

View file

@ -109,6 +109,11 @@
<artifactId>slf4j-log4j12</artifactId>
<version>1.8.0-beta2</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>