Merge branch 'master' of github.com:magefree/mage into github-actions

This commit is contained in:
Phred 2022-03-04 13:36:36 -06:00
commit bef6c93d36
No known key found for this signature in database
GPG key ID: 8103F27168DAA2A0
3516 changed files with 79240 additions and 22247 deletions

View file

@ -11,6 +11,7 @@ import mage.client.util.sets.ConstructedFormats;
import mage.constants.CardType;
import mage.constants.ColoredManaSymbol;
import mage.constants.Rarity;
import mage.constants.SuperType;
import mage.util.RandomUtil;
import mage.util.TournamentUtil;
@ -153,7 +154,7 @@ public final class DeckGenerator {
final CardCriteria nonBasicLandCriteria = new CardCriteria();
nonBasicLandCriteria.setCodes(sets);
nonBasicLandCriteria.types(CardType.LAND);
nonBasicLandCriteria.notSupertypes("Basic");
nonBasicLandCriteria.notSupertypes(SuperType.BASIC);
// Generate basic land cards
Map<String, List<CardInfo>> basicLands = generateBasicLands(setsToUse);

View file

@ -40,8 +40,12 @@ import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.Locale;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static java.lang.Math.min;
import static org.mage.plugins.card.images.DownloadPicturesService.getTokenCardUrls;
@ -515,39 +519,30 @@ public class MageBook extends JComponent {
}
// cards stats
int startNumber = 9999;
int endNumber = 0;
List<Integer> haveNumbers = set
.getSetCardInfo()
.stream()
.map(ExpansionSet.SetCardInfo::getCardNumberAsInt)
.collect(Collectors.toList());
List<ExpansionSet.SetCardInfo> cards = set.getSetCardInfo();
// first run for numbers list
LinkedList<Integer> haveNumbers = new LinkedList<>();
for (ExpansionSet.SetCardInfo card : cards) {
int cardNumber = card.getCardNumberAsInt();
// skip xmage special numbers for cards (TODO: replace full art cards numbers from 180+20 to 180b, 180c and vice versa like scryfall)
if (cardNumber > 500) {
continue;
}
startNumber = min(startNumber, cardNumber);
endNumber = Math.max(endNumber, cardNumber);
haveNumbers.add(cardNumber);
}
int startNumber = haveNumbers
.stream()
.min(Integer::compareTo)
.orElse(9999);
int endNumber = haveNumbers
.stream()
.max(Integer::compareTo)
.orElse(0);
// second run for empty numbers
int countHave = haveNumbers.size();
int countNotHave = 0;
if (!cards.isEmpty()) {
for (int i = startNumber; i <= endNumber; i++) {
if (!haveNumbers.contains(i)) {
countNotHave++;
}
}
}
int countNotHave = IntStream
.range(startNumber, endNumber + 1)
.map(x -> haveNumbers.contains(x) ? 0 : 1)
.sum();
// result
setInfo.setText(String.format("Have %d cards of %d", countHave, countHave + countNotHave));
setInfo.setText(String.format("%d cards of %d are available", countHave, countHave + countNotHave));
if (countNotHave > 0) {
setInfo.setForeground(new Color(150, 0, 0));
} else {

View file

@ -6,6 +6,7 @@ import mage.abilities.icon.CardIconImpl;
import mage.abilities.icon.CardIconOrder;
import mage.abilities.icon.CardIconPosition;
import mage.abilities.icon.CardIconType;
import mage.abilities.keyword.TransformAbility;
import mage.cards.*;
import mage.cards.decks.Deck;
import mage.cards.repository.CardInfo;
@ -109,7 +110,7 @@ public class TestCardRenderDialog extends MageDialog {
this.removeDialog();
}
private PermanentView createPermanentCard(Game game, UUID controllerId, String code, String cardNumber, int power, int toughness, int damage, boolean tapped, List<Ability> extraAbilities) {
private PermanentView createPermanentCard(Game game, UUID controllerId, String code, String cardNumber, int power, int toughness, int damage, boolean tapped, boolean transform, List<Ability> extraAbilities) {
CardInfo cardInfo = CardRepository.instance.findCard(code, cardNumber);
ExpansionInfo setInfo = ExpansionRepository.instance.getSetByCode(code);
CardSetInfo testSet = new CardSetInfo(cardInfo.getName(), setInfo.getCode(), cardNumber, cardInfo.getRarity(),
@ -120,21 +121,22 @@ public class TestCardRenderDialog extends MageDialog {
cardsList.add(newCard);
game.loadCards(cardsList, controllerId);
Card permCard = CardUtil.getDefaultCardSideForBattlefield(newCard);
Card permCard = CardUtil.getDefaultCardSideForBattlefield(game, newCard);
if (extraAbilities != null) {
extraAbilities.forEach(ability -> permCard.addAbility(ability));
}
PermanentCard perm = new PermanentCard(permCard, controllerId, game);
if (transform) {
// need direct transform call to keep other side info (original)
TransformAbility.transformPermanent(perm, permCard.getSecondCardFace(), game, null);
}
if (damage > 0) perm.damage(damage, controllerId, null, game);
if (power > 0) perm.getPower().setValue(power);
if (toughness > 0) perm.getToughness().setValue(toughness);
perm.removeSummoningSickness();
perm.setTapped(tapped);
if (perm.isTransformable()) {
perm.setTransformed(true);
}
PermanentView cardView = new PermanentView(perm, permCard, controllerId, game);
return cardView;
@ -151,7 +153,7 @@ public class TestCardRenderDialog extends MageDialog {
cardsList.add(newCard);
game.loadCards(cardsList, controllerId);
Card permCard = CardUtil.getDefaultCardSideForBattlefield(newCard);
Card permCard = CardUtil.getDefaultCardSideForBattlefield(game, newCard);
PermanentCard perm = new PermanentCard(permCard, controllerId, game);
perm.setFaceDown(true, game);
@ -290,16 +292,16 @@ public class TestCardRenderDialog extends MageDialog {
//*/
/* //test emblems
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "78", 125, 89, 0, false, null)); // Noxious Groodion
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "14", 3, 5, 2, false, null)); // Knight of Sorrows
cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 5, 2, 2, false, null)); // Huntmaster of the Fells, transforms
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "221", 0, 0, 0, false, null)); // Bedeck // Bedazzle
cardViews.add(createPermanentCard(game, playerYou.getId(), "XLN", "234", 0, 0, 0, false, null)); // Conqueror's Galleon
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "78", 125, 89, 0, false, false, null)); // Noxious Groodion
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "14", 3, 5, 2, false, false, null)); // Knight of Sorrows
cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 5, 2, 2, false, false, null)); // Huntmaster of the Fells, transforms
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "221", 0, 0, 0, false, false, null)); // Bedeck // Bedazzle
cardViews.add(createPermanentCard(game, playerYou.getId(), "XLN", "234", 0, 0, 0, false, false, null)); // Conqueror's Galleon
cardViews.add(createEmblem(new AjaniAdversaryOfTyrantsEmblem())); // Emblem Ajani
cardViews.add(createPlane(new AkoumPlane())); // Plane - Akoum
//*/
/* //test split, transform and mdf in hands
//test split, transform and mdf in hands
cardViews.add(createHandCard(game, playerYou.getId(), "SOI", "97")); // Accursed Witch
cardViews.add(createHandCard(game, playerYou.getId(), "UMA", "225")); // Fire // Ice
cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "14")); // Giant Killer
@ -309,10 +311,10 @@ public class TestCardRenderDialog extends MageDialog {
//* //test card icons
cardViews.add(createHandCard(game, playerYou.getId(), "POR", "169")); // Grizzly Bears
cardViews.add(createHandCard(game, playerYou.getId(), "DKA", "140")); // Huntmaster of the Fells, transforms
cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "401", 1, 1, 0, false, additionalIcons)); // Hinterland Drake
cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "1441", 1, 1, 0, true, additionalIcons)); // Kathari Remnant
cardViews.add(createPermanentCard(game, playerYou.getId(), "KHM", "50", 1, 1, 0, true, additionalIcons)); // Cosima, God of the Voyage
cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 3, 3, 1, false, true, additionalIcons)); // Huntmaster of the Fells, transforms
cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "401", 1, 1, 0, false, false, additionalIcons)); // Hinterland Drake
//cardViews.add(createPermanentCard(game, playerYou.getId(), "MB1", "1441", 1, 1, 0, true, false, additionalIcons)); // Kathari Remnant
//cardViews.add(createPermanentCard(game, playerYou.getId(), "KHM", "50", 1, 1, 0, true, false, additionalIcons)); // Cosima, God of the Voyage
//*/

View file

@ -499,7 +499,7 @@ public class MageActionCallback implements ActionCallback {
}
popupTextWindowOpen = true;
Image image = cardPanel.getImage();
displayCardInfo(cardPanel, image, bigCard);
displayCardInfo(cardPanel.getOriginal(), image, bigCard);
}
}
} else {
@ -671,7 +671,9 @@ public class MageActionCallback implements ActionCallback {
popupContainer.setLocation(location);
popupContainer.setVisible(true);
// popup hint mode
Image image = null;
CardView displayCard = cardPanel.getOriginal();
switch (enlargeMode) {
case COPY:
if (cardView instanceof PermanentView) {
@ -687,6 +689,7 @@ public class MageActionCallback implements ActionCallback {
image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal());
} else {
image = ImageCache.getImageOriginalAlternateName(cardView);
displayCard = displayCard.getSecondCardFace();
}
}
break;
@ -697,7 +700,7 @@ public class MageActionCallback implements ActionCallback {
image = cardPanel.getImage();
}
// shows the card in the popup Container
displayCardInfo(cardPanel, image, (BigCard) cardPreviewPane);
displayCardInfo(displayCard, image, (BigCard) cardPreviewPane);
} else {
logger.warn("No Card preview Pane in Mage Frame defined. Card: " + cardView.getName());
@ -709,21 +712,21 @@ public class MageActionCallback implements ActionCallback {
});
}
private void displayCardInfo(MageCard mageCard, Image image, BigCard bigCard) {
private void displayCardInfo(CardView card, Image image, BigCard bigCard) {
if (image instanceof BufferedImage) {
// XXX: scaled to fit width
bigCard.setCard(mageCard.getOriginal().getId(), enlargeMode, image, mageCard.getOriginal().getRules(), mageCard.getOriginal().isToRotate());
bigCard.setCard(card.getId(), enlargeMode, image, card.getRules(), card.isToRotate());
// if it's an ability, show only the ability text as overlay
if (mageCard.getOriginal().isAbility() && enlargeMode == EnlargeMode.NORMAL && isAbilityTextOverlayEnabled()) {
if (card.isAbility() && enlargeMode == EnlargeMode.NORMAL && isAbilityTextOverlayEnabled()) {
bigCard.showTextComponent();
} else {
bigCard.hideTextComponent();
}
} else {
JXPanel panel = GuiDisplayUtil.getDescription(mageCard.getOriginal(), bigCard.getWidth(), bigCard.getHeight());
JXPanel panel = GuiDisplayUtil.getDescription(card, bigCard.getWidth(), bigCard.getHeight());
panel.setVisible(true);
bigCard.hideTextComponent();
bigCard.addJXPanel(mageCard.getOriginal().getId(), panel);
bigCard.addJXPanel(card.getId(), panel);
}
enlargeredViewOpened = new Date();
}

View file

@ -49,12 +49,10 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
private static final float ROT_CENTER_TO_BOTTOM_CORNER = 0.7071067811865475244008443621048f;
private CardView gameCard; // current card side, can be different for double faces cards (it's a gui sides, not mtg - so mdf cards will have second side too)
private CardView cardSideMain = null; // for gui card side switch
private CardView cardSideOther = null; // for gui card side switch
private CardView updateCard;
// if null then gameCard contains main card
// if not null then gameCard contains second side
private CardView temporary;
private double tappedAngle = 0;
private double flippedAngle = 0;
@ -98,7 +96,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
public double transformAngle = 1;
private boolean transformed;
private boolean guiTransformed; // current main or other side in gui (use isTransformed() to find a real transform state)
private boolean animationInProgress = false;
private Container cardContainer;
@ -115,6 +113,8 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
// Store away params
this.setGameCard(newGameCard);
this.setGameCardSides(newGameCard);
this.callback = callback;
this.gameId = gameId;
this.needFullPermanentRender = needFullPermanentRender;
@ -153,16 +153,24 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
// Create the day night button
dayNightButton = new JButton("");
dayNightButton.setSize(32, 32);
dayNightButton.setToolTipText("This permanent is a double faced card. To see the back face card, push this button or turn mouse wheel down while hovering with the mouse pointer over the permanent.");
dayNightButton.setToolTipText("This permanent is a double faced card. To see the another face card, push this button or move mouse wheel down while hovering over it.");
BufferedImage day = ImageManagerImpl.instance.getDayImage();
dayNightButton.setIcon(new ImageIcon(day));
dayNightButton.addActionListener(e -> {
// if card is being rotated, ignore action performed
// if card is tapped, no visual transforming is possible (implementation limitation)
// if card is permanent, it will be rotated by Mage, so manual rotate should be possible
if (animationInProgress || isTapped() || isPermanent) {
// if card is rotating then ignore it
if (animationInProgress) {
return;
}
// if card is tapped then no visual transforming is possible, so switch it immediately
if (isTapped()) {
// toggle without animation
this.getTopPanelRef().toggleTransformed();
this.getTopPanelRef().repaint();
return;
}
// normal animation
Animation.transformCard(this);
});
@ -228,7 +236,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
public final void initialDraw() {
// Kick off
if (getGameCard().isTransformed()) {
if (getGameCard().isTransformed() && !this.isTransformed()) {
// this calls updateImage
toggleTransformed();
} else {
@ -470,7 +478,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override
public final boolean isTapped() {
if (isPermanent) {
if (isPermanent && getGameCard() instanceof PermanentView) {
return ((PermanentView) getGameCard()).isTapped();
}
return false;
@ -478,7 +486,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override
public final boolean isFlipped() {
if (isPermanent) {
if (isPermanent && getGameCard() instanceof PermanentView) {
return ((PermanentView) getGameCard()).isFlipped();
}
return false;
@ -486,14 +494,10 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override
public final boolean isTransformed() {
if (isPermanent) {
if (getGameCard().isTransformed()) {
return !this.transformed;
} else {
return this.transformed;
}
if (this.cardSideMain.isTransformed()) {
return !this.guiTransformed;
} else {
return this.transformed;
return this.guiTransformed;
}
}
@ -521,7 +525,9 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
return;
}
if (transformed && card.equals(this.temporary)) {
// card param can be any card side (example: user switch a card side by transform button)
if (guiTransformed && card.equals(this.cardSideMain)) {
// update can be called from different places (after transform click, after selection change, etc)
// if card temporary transformed before (by icon click) then do not update full data (as example, after selection changed)
this.isChoosable = card.isChoosable();
@ -542,7 +548,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
AudioManager.playTapPermanent();
}
boolean needsTranforming = isTransformed() != card.isTransformed();
if (needsTranforming) {
if (needsTranforming && !animationInProgress) {
Animation.transformCard(this);
}
}
@ -558,6 +564,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
// Set the new card
this.setGameCard(card);
this.setGameCardSides(card);
// Update tooltip text
String cardType = getType(card);
@ -569,29 +576,21 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
}
// Update transform circle
if (card.canTransform()) {
if (card.canTransform() && dayNightButton != null) {
BufferedImage transformIcon;
if (isTransformed() || card.isTransformed()) {
if (this.isTransformed()) {
transformIcon = ImageManagerImpl.instance.getNightImage();
} else {
transformIcon = ImageManagerImpl.instance.getDayImage();
}
if (dayNightButton != null) {
dayNightButton.setVisible(!isPermanent);
dayNightButton.setIcon(new ImageIcon(transformIcon));
}
dayNightButton.setVisible(true); // show T button for any cards and permanents
dayNightButton.setIcon(new ImageIcon(transformIcon));
}
}
@Override
public CardView getOriginal() {
if (this.temporary == null) {
// current side: main, return: main
return this.getGameCard();
} else {
// current side: second, return: main
return this.temporary;
}
return this.cardSideMain;
}
@Override
@ -827,16 +826,14 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override
public PermanentView getOriginalPermanent() {
// used in battlefield drawing, sorting and other GUI things
// users can switch current permanent to another side, but you must return original permanent here all the time
if (isPermanent) {
return (PermanentView) this.getGameCard();
return (PermanentView) this.cardSideMain;
}
throw new IllegalStateException("Is not permanent.");
}
public void setTransformed(boolean transformed) {
this.transformed = transformed;
}
private void copySelections(CardView source, CardView dest) {
if (source != null && dest != null) {
dest.setSelected(source.isSelected());
@ -847,40 +844,31 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
@Override
public void toggleTransformed() {
this.transformed = !this.transformed;
if (transformed) {
// show night card
if (dayNightButton != null) { // if transformbable card is copied, button can be null
BufferedImage night = ImageManagerImpl.instance.getNightImage();
dayNightButton.setIcon(new ImageIcon(night));
}
if (this.getGameCard().getSecondCardFace() == null) {
// VIEW mode (user can change card side at any time by n/d button)
this.guiTransformed = !this.guiTransformed;
if (dayNightButton != null) { // if transformbable card is copied, button can be null
BufferedImage image = this.isTransformed() ? ImageManagerImpl.instance.getNightImage() : ImageManagerImpl.instance.getDayImage();
dayNightButton.setIcon(new ImageIcon(image));
}
// switch card data
if (this.guiTransformed) {
// main side -> alternative side
if (this.cardSideOther == null) {
logger.error("no second side for card to transform!");
return;
}
if (!isPermanent) { // use only for custom transformation (when pressing day-night button)
copySelections(this.getGameCard(), this.getGameCard().getSecondCardFace());
this.setTemporary(this.getGameCard());
update(this.getGameCard().getSecondCardFace());
}
copySelections(this.cardSideMain, this.cardSideOther);
update(this.cardSideOther);
this.getGameCard().setAlternateName(this.cardSideMain.getName());
} else {
// show day card
if (dayNightButton != null) { // if transformbable card is copied, button can be null
BufferedImage day = ImageManagerImpl.instance.getDayImage();
dayNightButton.setIcon(new ImageIcon(day));
}
if (!isPermanent) { // use only for custom transformation (when pressing day-night button)
copySelections(this.getGameCard().getSecondCardFace(), this.getGameCard());
update(this.getTemporary());
this.setTemporary(null);
}
// alternative side -> main side
copySelections(this.cardSideOther, this.cardSideMain);
update(this.cardSideMain);
this.getGameCard().setAlternateName(this.cardSideOther.getName());
}
// switch card names for render
String temp = this.getGameCard().getAlternateName();
this.getGameCard().setAlternateName(this.getGameCard().getOriginalName());
this.getGameCard().setOriginalName(temp);
updateArtImage();
}
@ -939,6 +927,32 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
this.gameCard = gameCard;
}
private void setGameCardSides(CardView gameCard) {
if (this.cardSideMain == null) {
// new card
this.cardSideMain = gameCard;
this.cardSideOther = gameCard.getSecondCardFace();
} else {
// updated card
if (this.cardSideMain.getName().equals(gameCard.getName())) {
// from main side
this.cardSideMain = gameCard;
this.cardSideOther = gameCard.getSecondCardFace();
} else {
// from other side
this.cardSideOther = gameCard;
return;
}
}
// fix other side: if it's a night side permanent then the main side info must be extracted
if (this.cardSideOther == null || this.cardSideOther.getName().equals(this.cardSideMain.getName())) {
if (this.cardSideMain instanceof PermanentView) {
this.cardSideOther = ((PermanentView) this.cardSideMain).getOriginal();
}
}
}
public CardView getUpdateCard() {
return updateCard;
}
@ -947,14 +961,6 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
this.updateCard = updateCard;
}
public CardView getTemporary() {
return temporary;
}
public void setTemporary(CardView temporary) {
this.temporary = temporary;
}
public double getTappedAngle() {
return tappedAngle;
}
@ -986,6 +992,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
needLocation.getCardHeight()
);
Rectangle animatedRect = MageLayer.animateCoords(this, normalRect);
return animatedRect.contains(x, y);
}

View file

@ -774,7 +774,7 @@ public class CardPanelRenderModeImage extends CardPanel {
private BufferedImage getFaceDownImage() {
// TODO: add download default images
if (isPermanent()) {
if (isPermanent() && getGameCard() instanceof PermanentView) {
if (((PermanentView) getGameCard()).isMorphed()) {
return ImageCache.getMorphImage();
} else {

View file

@ -409,7 +409,7 @@ public class CardPanelRenderModeMTGO extends CardPanel {
private BufferedImage getFaceDownImage() {
// TODO: add download default images
if (isPermanent()) {
if (isPermanent() && getGameCard() instanceof PermanentView) {
if (((PermanentView) getGameCard()).isMorphed()) {
return ImageCache.getMorphImage();
} else {

View file

@ -244,7 +244,7 @@ public final class CardRendererUtils {
// boost colorizing
if (value != null) {
int current = value.getValue();
int origin = value.getBaseValue();
int origin = value.getBaseValueModified();
if (origin != 0) {
if (current < origin) {
return textLight ? CARD_TEXT_COLOR_BAD_LIGHT : CARD_TEXT_COLOR_BAD_DARK;

View file

@ -1105,7 +1105,7 @@ public class ModernCardRenderer extends CardRenderer {
g.setColor(defaultTextColor);
g.drawString(ptText2, ptPosStart2, curY - ptTextOffset - 1); // center
// t
g.setColor(CardRendererUtils.getCardTextColor(cardView.getOriginalCard().getPower(), CardRendererUtils.isCardWithDamage(cardView), defaultTextColor, defaultTextLight));
g.setColor(CardRendererUtils.getCardTextColor(cardView.getOriginalCard().getToughness(), CardRendererUtils.isCardWithDamage(cardView), defaultTextColor, defaultTextLight));
g.drawString(ptText3, ptPosStart3, curY - ptTextOffset - 1); // right
//
g.setColor(defaultTextColor);

View file

@ -68,7 +68,7 @@ public class GathererSets implements Iterable<DownloadJob> {
// "PALP" -- Gatherer does not have the set Asia Pacific Land Program
// "ATH" -- has cards from many sets, symbol does not exist on gatherer
// "CP", "DPA", "PELP", "PGPX", "PGRU", "H17", "JR", "SWS", // need to fix
"H09", "PD2", "PD3", "UNH", "CM1", "V11", "A25", "UST", "IMA", "DD2", "EVG", "DDC", "DDE", "DDD", "8EB", "9EB", "CHR", "G18", "GVL", "S00", "S99", "UGL" // ok
"H09", "PD2", "PD3", "UNH", "CM1", "V11", "A25", "UST", "IMA", "DD2", "EVG", "DDC", "DDE", "DDD", "CHR", "G18", "GVL", "S00", "S99", "UGL" // ok
// current testing
};
@ -160,8 +160,6 @@ public class GathererSets implements Iterable<DownloadJob> {
codeReplacements.put("USG", "UZ");
codeReplacements.put("VIS", "VI");
codeReplacements.put("WTH", "WL");
codeReplacements.put("8EB", "8ED"); // inner xmage set for 8th edition
codeReplacements.put("9EB", "8ED"); // inner xmage set for 9th edition
}
public GathererSets() {
@ -175,15 +173,6 @@ public class GathererSets implements Iterable<DownloadJob> {
// checks for wrong card settings and support (easy to control what all good)
private static final HashMap<String, CheckResult> setsToDownload = new HashMap<>();
private static final HashMap<String, String> codesToIgnoreCheck = new HashMap<>();
static {
// xMage have inner sets for 8th and 9th Edition for booster workaround (cards from core game do not include in boosters)
// see https://mtg.gamepedia.com/8th_Edition/Core_Game
// check must ignore that sets
codesToIgnoreCheck.put("8EB", "8th Edition Box");
codesToIgnoreCheck.put("9EB", "9th Edition Box");
}
private void CheckSearchResult(String searchCode, ExpansionSet foundedExp, boolean canDownloadTask,
boolean haveCommon, boolean haveUncommon, boolean haveRare, boolean haveMyth) {
@ -220,11 +209,6 @@ public class GathererSets implements Iterable<DownloadJob> {
for (ExpansionSet set : Sets.getInstance().values()) {
// ignore some inner sets
if (codesToIgnoreCheck.get(set.getCode()) != null) {
continue;
}
CheckResult res = setsToDownload.get(set.getCode());
// 1. not configured at all
@ -259,7 +243,7 @@ public class GathererSets implements Iterable<DownloadJob> {
}
}
// 4. info: sets with missing cards for boosters (todo: what about +20 number for alternative land arts?)
// 4. info: sets with missing cards for boosters
if (set.getMaxCardNumberInBooster() != Integer.MAX_VALUE) {
for (ExpansionSet.SetCardInfo card : set.getSetCardInfo()) {
if (card.getCardNumberAsInt() > set.getMaxCardNumberInBooster()) {

View file

@ -28,6 +28,7 @@ public class GathererSymbols implements Iterable<DownloadJob> {
private static final String[] symbols = {"W", "U", "B", "R", "G",
"W/U", "U/B", "B/R", "R/G", "G/W", "W/B", "U/R", "B/G", "R/W", "G/U",
"W/U/P", "U/B/P", "B/R/P", "R/G/P", "G/W/P", "W/B/P", "U/R/P", "B/G/P", "R/W/P", "G/U/P",
"2/W", "2/U", "2/B", "2/R", "2/G",
"WP", "UP", "BP", "RP", "GP",
"X", "S", "T", "Q", "C", "E"};

View file

@ -74,16 +74,22 @@ public enum ScryfallImageSource implements CardImageSource {
// CARDS TRY
// direct links to images via hardcoded API path. Used for cards with non-ASCII collector numbers
// direct links to images via hardcoded API path
// used for cards with non-ASCII collector numbers or another use cases
if (baseUrl == null) {
String apiUrl = ScryfallImageSupportCards.findDirectDownloadLink(card.getSet(), card.getName(), card.getCollectorId());
if (apiUrl != null) {
baseUrl = apiUrl + localizedCode + "?format=image";
alternativeUrl = apiUrl + defaultCode + "?format=image";
// workaround to use cards without english images (some promos or special cards)
if (Objects.equals(baseUrl, alternativeUrl) && baseUrl.endsWith("/en?format=image")) {
alternativeUrl = alternativeUrl.replace("/en?format=image", "/?format=image");
String link = ScryfallImageSupportCards.findDirectDownloadLink(card.getSet(), card.getName(), card.getCollectorId());
if (link != null) {
if (ScryfallImageSupportCards.isApiLink(link)) {
// api
baseUrl = link + localizedCode + "?format=image";
alternativeUrl = link + defaultCode + "?format=image";
// workaround to use cards without english images (some promos or special cards)
if (Objects.equals(baseUrl, alternativeUrl) && baseUrl.endsWith("/en?format=image")) {
alternativeUrl = alternativeUrl.replace("/en?format=image", "/?format=image");
}
} else {
// image
baseUrl = link;
}
}
}
@ -103,18 +109,22 @@ public enum ScryfallImageSource implements CardImageSource {
// basic cards by api call (redirect to img link)
// example: https://api.scryfall.com/cards/xln/121/en?format=image
if (baseUrl == null) {
baseUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet(), isToken) + "/"
+ card.getCollectorId() + "/" + localizedCode + "?format=image";
alternativeUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet(), isToken) + "/"
+ card.getCollectorId() + "/" + defaultCode + "?format=image";
baseUrl = String.format("https://api.scryfall.com/cards/%s/%s/%s?format=image",
formatSetName(card.getSet(), isToken),
card.getCollectorId(),
localizedCode);
alternativeUrl = String.format("https://api.scryfall.com/cards/%s/%s/%s?format=image",
formatSetName(card.getSet(), isToken),
card.getCollectorId(),
defaultCode);
// workaround to use cards without english images (some promos or special cards)
// bug: https://github.com/magefree/mage/issues/6829
// example: Mysterious Egg from IKO https://api.scryfall.com/cards/iko/385/?format=image
if (Objects.equals(baseUrl, alternativeUrl)) {
// without loc code scryfall must return first available image
alternativeUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet(), isToken) + "/"
+ card.getCollectorId() + "/?format=image";
alternativeUrl = String.format("https://api.scryfall.com/cards/%s/%s/?format=image",
formatSetName(card.getSet(), isToken),
card.getCollectorId());
}
}
@ -126,6 +136,7 @@ public enum ScryfallImageSource implements CardImageSource {
final String localizedCode = languageAliases.getOrDefault(this.getCurrentLanguage(), defaultCode);
List<String> needUrls = new ArrayList<>();
int needFaceIndex = card.isSecondSide() ? 1 : 0;
String apiUrl = ScryfallImageSupportCards.findDirectDownloadLink(card.getSet(), card.getName(), card.getCollectorId());
if (apiUrl != null) {
@ -143,11 +154,15 @@ public enum ScryfallImageSource implements CardImageSource {
} else {
// BY CARD NUMBER
// localized and default
needUrls.add("https://api.scryfall.com/cards/"
+ formatSetName(card.getSet(), isToken) + "/" + card.getCollectorId() + "/" + localizedCode);
needUrls.add(String.format("https://api.scryfall.com/cards/%s/%s/%s",
formatSetName(card.getSet(), isToken),
card.getCollectorId(),
localizedCode));
if (!localizedCode.equals(defaultCode)) {
needUrls.add("https://api.scryfall.com/cards/"
+ formatSetName(card.getSet(), isToken) + "/" + card.getCollectorId() + "/" + defaultCode);
needUrls.add(String.format("https://api.scryfall.com/cards/%s/%s/%s",
formatSetName(card.getSet(), isToken),
card.getCollectorId(),
defaultCode));
}
}
@ -178,8 +193,11 @@ public enum ScryfallImageSource implements CardImageSource {
if (jsonFaces == null) {
throw new MageException("Couldn't find card_faces in card's JSON data: " + jsonUrl);
}
if (jsonFaces.size() < needFaceIndex + 1) {
throw new MageException("card_faces doesn't contains face index in card's JSON data: " + jsonUrl);
}
JsonObject jsonFace = jsonFaces.get(card.isSecondSide() ? 1 : 0).getAsJsonObject();
JsonObject jsonFace = jsonFaces.get(needFaceIndex).getAsJsonObject();
JsonObject jsonImages = JsonUtil.getAsObject(jsonFace, "image_uris");
if (jsonImages == null) {
throw new MageException("Couldn't find image_uris in card's JSON data: " + jsonUrl);

View file

@ -1,6 +1,5 @@
package org.mage.plugins.card.dl.sources;
import com.google.common.collect.ImmutableMap;
import org.tritonus.share.ArraySet;
import java.util.HashMap;
@ -16,10 +15,6 @@ import java.util.regex.Pattern;
*/
public class ScryfallImageSupportCards {
private static final Map<String, String> xmageSetsToScryfall = ImmutableMap.<String, String>builder()
.put("8EB", "8ED")
.put("9EB", "9ED")
.build();
static final Pattern REGEXP_DIRECT_KEY_SET_CODE_PATTERN = Pattern.compile("(\\w+)\\/", Pattern.MULTILINE);
static final Pattern REGEXP_DIRECT_KEY_CARD_NAME_PATTERN = Pattern.compile("\\/(.+?)\\/", Pattern.MULTILINE);
@ -125,7 +120,6 @@ public class ScryfallImageSupportCards {
add("LGN"); // Legions
add("SCG"); // Scourge
add("8ED"); // Eighth Edition
add("8EB"); // Eighth Edition Box
add("WC03"); // World Championship Decks 2003
add("MRD"); // Mirrodin
add("PAL04"); // Arena League 2004
@ -145,7 +139,6 @@ public class ScryfallImageSupportCards {
add("BOK"); // Betrayers of Kamigawa
add("SOK"); // Saviors of Kamigawa
add("9ED"); // Ninth Edition
add("9EB"); // Ninth Edition Box
//add("PSAL"); // Salvat 2005
add("RAV"); // Ravnica: City of Guilds
add("P2HG"); // Two-Headed Giant Tournament
@ -196,7 +189,7 @@ public class ScryfallImageSupportCards {
add("ALA"); // Shards of Alara
add("PALA"); // Shards of Alara Promos
add("DD2"); // Duel Decks: Jace vs. Chandra
add("PWP09"); // Wizards Play Network 2009
add("PW09"); // Wizards Play Network 2009
add("PDTP"); // Duels of the Planeswalkers 2009 Promos
//add("PMPS09"); // Magic Premiere Shop 2009
add("P09"); // Magic Player Rewards 2009
@ -216,7 +209,7 @@ public class ScryfallImageSupportCards {
add("DDD"); // Duel Decks: Garruk vs. Liliana
add("H09"); // Premium Deck Series: Slivers
add("PDP10"); // Duels of the Planeswalkers 2010 Promos
add("PWP10"); // Wizards Play Network 2010
add("PW10"); // Wizards Play Network 2010
//add("PMPS10"); // Magic Premiere Shop 2010
add("P10"); // Magic Player Rewards 2010
add("G10"); // Judge Gift Cards 2010
@ -237,7 +230,7 @@ public class ScryfallImageSupportCards {
add("TD0"); // Magic Online Theme Decks
add("PD2"); // Premium Deck Series: Fire and Lightning
//add("PMPS11"); // Magic Premiere Shop 2011
add("PWP11"); // Wizards Play Network 2011
add("PW11"); // Wizards Play Network 2011
//add("PS11"); // Salvat 2011
add("P11"); // Magic Player Rewards 2011
add("G11"); // Judge Gift Cards 2011
@ -259,7 +252,7 @@ public class ScryfallImageSupportCards {
add("ISD"); // Innistrad
add("PD3"); // Premium Deck Series: Graveborn
add("PIDW"); // IDW Comics 2012
add("PWP12"); // Wizards Play Network 2012
add("PW12"); // Wizards Play Network 2012
add("PDP12"); // Duels of the Planeswalkers 2012 Promos
add("J12"); // Judge Gift Cards 2012
add("F12"); // Friday Night Magic 2012
@ -506,28 +499,38 @@ public class ScryfallImageSupportCards {
add("AFR"); // Adventures in the Forgotten Realms
add("AFC"); // Forgotten Realms Commander
add("J21"); // Jumpstart: Historic Horizons
add("MID"); // Innistrad, Midnight Hunt
add("MID"); // Innistrad: Midnight Hunt
add("MIC"); // Midnight Hunt Commander
add("VOW"); // Innistrad: Crimson Vow
add("VOC"); // Crimson Vow Commander
add("Y22"); // Alchemy: Innistrad
add("DBL"); // Innistrad: Double Feature
add("NEO"); // Kamigawa: Neon Dynasty
add("NEC"); // Neon Dynasty Commander
add("SNC"); // Streets of New Capenna
add("SLX"); // Universes Within
}
};
private static final Map<String, String> directDownloadLinks = new HashMap<String, String>() {
{
// xmage card -> api link:
// examples:
// api example: https://api.scryfall.com/cards/trix/6/
// api format is primary
// xmage card -> api or image link
// WARNING, try use api links as much as possible (it supports build-in translation)
//
// code form for one card:
// example:
// api link: https://api.scryfall.com/cards/trix/6/
// image link: https://c1.scryfall.com/file/scryfall-cards/large/back/d/5/d5dfd236-b1da-4552-b94f-ebf6bb9dafdf.jpg
//
// key for one card:
// set/card_name
//
// code form for same name cards (alternative images):
// set/card_name/card_number
// set/card_name/card_number
// key for same name cards (alternative images):
// set/card_name/card_number_1
// set/card_name/card_number_2
//
// Cards with non-ASCII collector numbers must use direct download (cause xmage uses different card number)
// Verify checks must check and show missing data from that list
// WARNING, must use as API link, not image
// 10E
put("10E/Air Elemental/64*", "https://api.scryfall.com/cards/10e/64★/");
put("10E/Anaba Bodyguard/187*", "https://api.scryfall.com/cards/10e/187★/");
@ -791,10 +794,6 @@ public class ScryfallImageSupportCards {
put("PM14/Hive Stirrings/21*", "https://api.scryfall.com/cards/pm14/21★/");
put("PM14/Megantic Sliver/185*", "https://api.scryfall.com/cards/pm14/185★/");
put("PM14/Ratchet Bomb/215*", "https://api.scryfall.com/cards/pm14/215★/");
// PROE
put("PROE/Emrakul, the Aeons Torn/4*", "https://api.scryfall.com/cards/proe/4★/");
put("PROE/Lord of Shatterskull Pass/156*", "https://api.scryfall.com/cards/proe/156★/");
//
// PMBS
put("PMBS/Glissa, the Traitor/96*", "https://api.scryfall.com/cards/pmbs/96★/");
put("PMBS/Hero of Bladehold/8*", "https://api.scryfall.com/cards/pmbs/8★/");
@ -953,11 +952,23 @@ public class ScryfallImageSupportCards {
put("WAR/Ugin, the Ineffable/2*", "https://api.scryfall.com/cards/war/2★/");
put("WAR/Vivien, Champion of the Wilds/180*", "https://api.scryfall.com/cards/war/180★/");
put("WAR/Vraska, Swarm's Eminence/236*", "https://api.scryfall.com/cards/war/236★/");
// SLD
// TODO: update direct image links in 2022 for HQ images
put("SLD/Zndrsplt, Eye of Wisdom/379", "https://api.scryfall.com/cards/sld/379/");
put("SLD/Zndrsplt, Eye of Wisdom/379b", "https://c1.scryfall.com/file/scryfall-cards/large/back/d/5/d5dfd236-b1da-4552-b94f-ebf6bb9dafdf.jpg");
put("SLD/Krark's Thumb/383", "https://api.scryfall.com/cards/sld/383/");
put("SLD/Krark's Thumb/383b", "https://c1.scryfall.com/file/scryfall-cards/large/back/9/f/9f63277b-e139-46c8-b9e3-0cfb647f44cc.jpg");
put("SLD/Okaun, Eye of Chaos/380", "https://api.scryfall.com/cards/sld/380/");
put("SLD/Okaun, Eye of Chaos/380b", "https://c1.scryfall.com/file/scryfall-cards/large/back/9/4/94eea6e3-20bc-4dab-90ba-3113c120fb90.jpg");
put("SLD/Propaganda/381", "https://api.scryfall.com/cards/sld/381/");
put("SLD/Propaganda/381b", "https://c1.scryfall.com/file/scryfall-cards/large/back/3/e/3e3f0bcd-0796-494d-bf51-94b33c1671e9.jpg");
put("SLD/Stitch in Time/382", "https://api.scryfall.com/cards/sld/382/");
put("SLD/Stitch in Time/382b", "https://c1.scryfall.com/file/scryfall-cards/large/back/0/8/087c3a0d-c710-4451-989e-596b55352184.jpg");
}
};
public static String findScryfallSetCode(String xmageCode) {
return xmageSetsToScryfall.getOrDefault(xmageCode, xmageCode).toLowerCase(Locale.ENGLISH);
return xmageCode.toLowerCase(Locale.ENGLISH);
}
public static Set<String> getSupportedSets() {
@ -987,8 +998,8 @@ public class ScryfallImageSupportCards {
}
public static String extractSetCodeFromDirectKey(String key) {
// from: 8EB/Giant Octopus
// to: 8EB
// from: 8ED/Giant Octopus
// to: 8ED
Matcher matcher = REGEXP_DIRECT_KEY_SET_CODE_PATTERN.matcher(key);
if (matcher.find()) {
return matcher.group(1);
@ -997,7 +1008,7 @@ public class ScryfallImageSupportCards {
}
public static String extractCardNameFromDirectKey(String key) {
// from: 8EB/Giant Octopus/
// from: 8ED/Giant Octopus/
// to: Giant Octopus
Matcher matcher = REGEXP_DIRECT_KEY_CARD_NAME_PATTERN.matcher(key + "/"); // add / for regexp workaround
if (matcher.find()) {
@ -1006,6 +1017,10 @@ public class ScryfallImageSupportCards {
return "";
}
public static boolean isApiLink(String link) {
return !link.endsWith(".jpg") && !link.endsWith(".png");
}
public static Map<String, String> getDirectDownloadLinks() {
return directDownloadLinks;
}

View file

@ -637,6 +637,81 @@ public class ScryfallImageSupportTokens {
put("AFR/Emblem Zariel, Archduke of Avernus", "https://api.scryfall.com/cards/tafr/19/en?format=image");
put("AFR/Zombie", "https://api.scryfall.com/cards/tafr/9/en?format=image");
// AFC
put("AFC/Angel", "https://api.scryfall.com/cards/tafc/1/en?format=image");
put("AFC/Beast", "https://api.scryfall.com/cards/tafc/7/en?format=image");
put("AFC/Clue", "https://api.scryfall.com/cards/tafc/10/en?format=image");
put("AFC/Dragon Spirit", "https://api.scryfall.com/cards/tafc/9/en?format=image");
put("AFC/Dragon", "https://api.scryfall.com/cards/tafc/6/en?format=image");
put("AFC/Illusion", "https://api.scryfall.com/cards/tafc/3/en?format=image");
put("AFC/Knight", "https://api.scryfall.com/cards/tafc/2/en?format=image");
put("AFC/Rat", "https://api.scryfall.com/cards/tafc/5/en?format=image");
put("AFC/Saproling", "https://api.scryfall.com/cards/tafc/8/en?format=image");
put("AFC/Servo", "https://api.scryfall.com/cards/tafc/11/en?format=image");
put("AFC/Thopter", "https://api.scryfall.com/cards/tafc/12/en?format=image");
// MIC
put("MIC/Beast", "https://api.scryfall.com/cards/tmic/7/en?format=image");
put("MIC/Centaur", "https://api.scryfall.com/cards/tmic/8/en?format=image");
put("MIC/Eldrazi Spawn", "https://api.scryfall.com/cards/tmic/1/en?format=image");
put("MIC/Elephant", "https://api.scryfall.com/cards/tmic/9/en?format=image");
put("MIC/Human Soldier", "https://api.scryfall.com/cards/tmic/2/en?format=image");
put("MIC/Knight", "https://api.scryfall.com/cards/tmic/3/en?format=image");
put("MIC/Rhino", "https://api.scryfall.com/cards/tmic/10/en?format=image");
put("MIC/Snake", "https://api.scryfall.com/cards/tmic/11/en?format=image");
put("MIC/Zombie Army", "https://api.scryfall.com/cards/tmic/6/en?format=image");
put("MIC/Zombie/1", "https://api.scryfall.com/cards/tmic/5/en?format=image"); // 2/2
put("MIC/Zombie/2", "https://api.scryfall.com/cards/tmic/4/en?format=image"); // */*
// MID
put("MID/Bat", "https://api.scryfall.com/cards/tmid/4/en?format=image");
put("MID/Beast", "https://api.scryfall.com/cards/tmid/8/en?format=image");
put("MID/Bird", "https://api.scryfall.com/cards/tmid/3/en?format=image");
put("MID/Clue", "https://api.scryfall.com/cards/tmid/16/en?format=image");
put("MID/Devil", "https://api.scryfall.com/cards/tmid/6/en?format=image");
put("MID/Elemental", "https://api.scryfall.com/cards/tmid/7/en?format=image");
put("MID/Human", "https://api.scryfall.com/cards/tmid/1/en?format=image");
put("MID/Insect", "https://api.scryfall.com/cards/tmid/9/en?format=image");
put("MID/Ooze", "https://api.scryfall.com/cards/tmid/10/en?format=image");
put("MID/Spider", "https://api.scryfall.com/cards/tmid/11/en?format=image");
put("MID/Spirit", "https://api.scryfall.com/cards/tmid/2/en?format=image");
put("MID/Emblem Teferi, Who Slows the Sunset", "https://api.scryfall.com/cards/tmid/17/en?format=image");
put("MID/Treefolk", "https://api.scryfall.com/cards/tmid/12/en?format=image");
put("MID/Vampire", "https://api.scryfall.com/cards/tmid/14/en?format=image");
put("MID/Wolf", "https://api.scryfall.com/cards/tmid/13/en?format=image");
put("MID/Emblem Wrenn and Seven", "https://api.scryfall.com/cards/tmid/18/en?format=image");
put("MID/Zombie/1", "https://api.scryfall.com/cards/tmid/5/en?format=image"); // decayed
put("MID/Zombie/2", "https://api.scryfall.com/cards/tmid/15/en?format=image"); // menace
// VOC
put("VOC/Angel", "https://api.scryfall.com/cards/tvoc/2/en?format=image");
put("VOC/Bat", "https://api.scryfall.com/cards/tvoc/4/en?format=image");
put("VOC/Clue", "https://api.scryfall.com/cards/tvoc/5/en?format=image");
put("VOC/Spirit/1", "https://api.scryfall.com/cards/tvoc/1/en?format=image"); // 1/1
put("VOC/Spirit/2", "https://api.scryfall.com/cards/tvoc/3/en?format=image"); // 3/3
put("VOC/Thopter", "https://api.scryfall.com/cards/tvoc/6/en?format=image");
// VOW
put("VOW/Blood", "https://api.scryfall.com/cards/tvow/17/en?format=image");
put("VOW/Boar", "https://api.scryfall.com/cards/tvow/12/en?format=image");
put("VOW/Emblem Chandra, Dressed to Kill", "https://api.scryfall.com/cards/tvow/20/en?format=image");
put("VOW/Dragon Illusion", "https://api.scryfall.com/cards/tvow/9/en?format=image");
put("VOW/Human Soldier", "https://api.scryfall.com/cards/tvow/15/en?format=image");
put("VOW/Human/1", "https://api.scryfall.com/cards/tvow/10/en?format=image"); // red
put("VOW/Human/2", "https://api.scryfall.com/cards/tvow/1/en?format=image"); // white
put("VOW/Insect", "https://api.scryfall.com/cards/tvow/13/en?format=image");
put("VOW/Slug", "https://api.scryfall.com/cards/tvow/6/en?format=image");
put("VOW/Spirit Cleric", "https://api.scryfall.com/cards/tvow/4/en?format=image");
put("VOW/Spirit/1", "https://api.scryfall.com/cards/tvow/2/en?format=image"); // 1/1
put("VOW/Spirit/2", "https://api.scryfall.com/cards/tvow/3/en?format=image"); // 4/4
put("VOW/Treasure", "https://api.scryfall.com/cards/tvow/18/en?format=image");
put("VOW/Vampire/1", "https://api.scryfall.com/cards/tvow/16/en?format=image"); // lifelink
put("VOW/Vampire/2", "https://api.scryfall.com/cards/tvow/7/en?format=image"); // flying, lifelink
put("VOW/Wolf/1", "https://api.scryfall.com/cards/tvow/14/en?format=image"); // green
put("VOW/Wolf/2", "https://api.scryfall.com/cards/tvow/11/en?format=image"); // red
put("VOW/Zombie/1", "https://api.scryfall.com/cards/tvow/8/en?format=image"); // 2/2
put("VOW/Zombie/2", "https://api.scryfall.com/cards/tvow/5/en?format=image"); // */*
// generate supported sets
supportedSets.clear();
for (String cardName : this.keySet()) {

View file

@ -42,6 +42,7 @@ public class ScryfallSymbolsSource implements Iterable<DownloadJob> {
// copy-past symbols list from gatherer download
private static final String[] SYMBOLS_LIST = {"W", "U", "B", "R", "G",
"W/U", "U/B", "B/R", "R/G", "G/W", "W/B", "U/R", "B/G", "R/W", "G/U",
"W/U/P", "U/B/P", "B/R/P", "R/G/P", "G/W/P", "W/B/P", "U/R/P", "B/G/P", "R/W/P", "G/U/P",
"2/W", "2/U", "2/B", "2/R", "2/G",
"WP", "UP", "BP", "RP", "GP",
"X", "S", "T", "Q", "C", "E"};

View file

@ -314,6 +314,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
private void reloadCardsToDownload(String selectedItem) {
// find selected sets
selectedSets.clear();
boolean onlyTokens = false;
List<String> formatSets;
List<String> sourceSets = selectedSource.getSupportedSets();
switch (selectedItem) {
@ -337,6 +338,8 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
break;
case ALL_TOKENS:
selectedSets.addAll(selectedSource.getSupportedSets());
onlyTokens = true;
break;
default:
@ -355,12 +358,14 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
for (CardDownloadData data : cardsMissing) {
if (data.isToken()) {
if (selectedSource.isTokenSource()
&& selectedSource.isTokenImageProvided(data.getSet(), data.getName(), data.getType())) {
&& selectedSource.isTokenImageProvided(data.getSet(), data.getName(), data.getType())
&& selectedSets.contains(data.getSet())) {
numberTokenImagesAvailable++;
cardsDownloadQueue.add(data);
}
} else {
if (selectedSource.isCardSource()
if (!onlyTokens
&& selectedSource.isCardSource()
&& selectedSource.isCardImageProvided(data.getSet(), data.getName())
&& selectedSets.contains(data.getSet())) {
numberCardImagesAvailable++;
@ -431,6 +436,8 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
String cardName = card.getName();
boolean isType2 = type2SetsFilter.contains(card.getSetCode());
CardDownloadData url = new CardDownloadData(cardName, card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", "", false, card.isDoubleFaced(), card.isNightCard());
// variations must have diff file names with additional postfix
if (url.getUsesVariousArt()) {
url.setDownloadName(createDownloadName(card));
}
@ -439,7 +446,10 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
url.setSplitCard(card.isSplitCard());
url.setType2(isType2);
// main side
allCardsUrls.add(url);
// second side (xmage's set doesn't have info about it, so generate it here)
if (card.isDoubleFaced()) {
if (card.getSecondSideName() == null || card.getSecondSideName().trim().isEmpty()) {
throw new IllegalStateException("Second side card can't have empty name.");

View file

@ -112,6 +112,9 @@
|Generate|EMBLEM:AFR|Lolth, Spider Queen||Emblem Lolth|LolthSpiderQueenEmblem|
|Generate|EMBLEM:AFR|Mordenkainen||Emblem Mordenkainen|MordenkainenEmblem|
|Generate|EMBLEM:AFR|Zariel, Archduke of Avernus||Emblem Zariel|ZarielArchdukeOfAvernusEmblem|
|Generate|EMBLEM:MID|Teferi, Who Slows the Sunset||Emblem Teferi|TeferiWhoSlowsTheSunsetEmblem|
|Generate|EMBLEM:MID|Wrenn and Seven||Emblem Wrenn|WrennAndSevenEmblem|
|Generate|EMBLEM:VOW|Chandra, Dressed to Kill||Emblem Chandra|ChandraDressedToKillEmblem|
# Planes
|Generate|PLANE:PCA|Plane - Academy at Tolaria West|||AcademyAtTolariaWestPlane|
@ -282,8 +285,8 @@
|Generate|TOK:BNG|Wolf|||WolfToken|
|Generate|TOK:BNG|Zombie|||ForlornPseudammaZombieToken|
|Generate|TOK:BOK|Snake|||SnakeToken|
|Generate|TOK:BOK|Spirit|||AnotherSpiritToken|
|Generate|TOK:BOK|Spirit|||SpiritToken|
|Generate|TOK:BOK|Spirit|1||AnotherSpiritToken|
|Generate|TOK:BOK|Spirit|2||SpiritToken|
|Generate|TOK:C13|Assembly-Worker|||AssembleWorkerToken|
|Generate|TOK:C13|Beast|1||BeastToken2|
|Generate|TOK:C13|Beast|2||CarnivoreToken|
@ -889,10 +892,9 @@
|Generate|TOK:MIR|Wall|||TidalWaveWallToken|
|Generate|TOK:MIR|Wall|||WoodToken|
|Generate|TOK:MIR|Zombie|||ZombieToken|
|Generate|TOK:MM2|Eldrazi Spawn|1|
|Generate|TOK:MM2|Eldrazi Spawn|2|
|Generate|TOK:MM2|Eldrazi Spawn|3|
|Generate|TOK:MM2|Eldrazi Spawn|||EldraziSpawnToken|
|Generate|TOK:MM2|Eldrazi Spawn|1||EldraziSpawnToken|
|Generate|TOK:MM2|Eldrazi Spawn|2||EldraziSpawnToken|
|Generate|TOK:MM2|Eldrazi Spawn|3||EldraziSpawnToken|
|Generate|TOK:MM2|Elephant|||ElephantToken|
|Generate|TOK:MM2|Faerie Rogue|||FaerieRogueToken|
|Generate|TOK:MM2|Germ|||PhyrexianGermToken|
@ -1589,4 +1591,77 @@
|Generate|TOK:AFR|Treasure|||TreasureToken|
|Generate|TOK:AFR|Vecna|||VecnaToken|
|Generate|TOK:AFR|Wolf|||WolfToken|
|Generate|TOK:AFR|Zombie|||ZombieToken|
|Generate|TOK:AFR|Zombie|||ZombieToken|
# AFC
|Generate|TOK:AFC|Angel|||AngelToken|
|Generate|TOK:AFC|Beast|||BeastToken|
# no need tokens for Eternalize ability, but scryfall have it: https://scryfall.com/card/tafc/4/champion-of-wits
|Generate|TOK:AFC|Clue|||ClueArtifactToken|
|Generate|TOK:AFC|Dragon|||DragonToken2|
|Generate|TOK:AFC|Dragon Spirit|||VrondissRageOfAncientsToken|
|Generate|TOK:AFC|Illusion|||MinnWilyIllusionistToken|
|Generate|TOK:AFC|Knight|||KnightToken|
|Generate|TOK:AFC|Rat|||RatToken|
|Generate|TOK:AFC|Saproling|||SaprolingToken|
|Generate|TOK:AFC|Servo|||ServoToken|
|Generate|TOK:AFC|Thopter|||ThopterColorlessToken|
# MIC
|Generate|TOK:MIC|Beast|||BeastToken|
|Generate|TOK:MIC|Centaur|||CentaurToken|
|Generate|TOK:MIC|Eldrazi Spawn|||EldraziSpawnToken|
|Generate|TOK:MIC|Elephant|||ElephantToken|
|Generate|TOK:MIC|Human Soldier|||HumanSoldierToken|
|Generate|TOK:MIC|Knight|||KnightToken|
|Generate|TOK:MIC|Rhino|||RhinoToken|
|Generate|TOK:MIC|Snake|||SnakeToken|
|Generate|TOK:MIC|Zombie|1||ZombieToken|
|Generate|TOK:MIC|Zombie|2||StitcherGeralfZombieToken|
|Generate|TOK:MIC|Zombie Army|||ZombieArmyToken|
# MID
|Generate|TOK:MID|Bat|||BatToken|
|Generate|TOK:MID|Beast|||BeastToken2|
|Generate|TOK:MID|Bird|||OminousRoostBirdToken|
|Generate|TOK:MID|Clue|||ClueArtifactToken|
|Generate|TOK:MID|Devil|||DevilToken|
|Generate|TOK:MID|Elemental|||SeizeTheStormElementalToken|
|Generate|TOK:MID|Human|||HumanToken|
|Generate|TOK:MID|Insect|||RiseOfTheAntsInsectToken|
|Generate|TOK:MID|Ooze|||ConsumingBlobOozeToken|
|Generate|TOK:MID|Spider|||SpiderToken|
|Generate|TOK:MID|Spirit|||SpiritWhiteToken|
|Generate|TOK:MID|Treefolk|||WrennAndSevenTreefolkToken|
|Generate|TOK:MID|Vampire|||HungryForMoreVampireToken|
|Generate|TOK:MID|Wolf|||WolfToken|
|Generate|TOK:MID|Zombie|1||ZombieDecayedToken|
|Generate|TOK:MID|Zombie|2||ZombieMenaceToken|
# VOC
|Generate|TOK:VOC|Angel|||AngelToken|
|Generate|TOK:VOC|Bat|||BatToken|
|Generate|TOK:VOC|Clue|||ClueArtifactToken|
|Generate|TOK:VOC|Spirit|1||SpiritToken|
|Generate|TOK:VOC|Spirit|2||AnotherSpiritToken|
|Generate|TOK:VOC|Thopter|||ThopterColorlessToken|
# VOW
|Generate|TOK:VOW|Blood|||BloodToken|
|Generate|TOK:VOW|Boar|||Boar3Token|
|Generate|TOK:VOW|Dragon Illusion|||DragonIllusionToken|
|Generate|TOK:VOW|Human|1||RedHumanToken|
|Generate|TOK:VOW|Human|2||HumanToken|
|Generate|TOK:VOW|Human Soldier|||HumanSoldierTrainingToken|
|Generate|TOK:VOW|Insect|||InsectToken|
|Generate|TOK:VOW|Slug|||SlugToken|
|Generate|TOK:VOW|Spirit|1||SpiritWhiteToken|
|Generate|TOK:VOW|Spirit|2||DorotheasRetributionSpiritToken|
|Generate|TOK:VOW|Spirit Cleric|||SpiritClericToken|
|Generate|TOK:VOW|Treasure|||TreasureToken|
|Generate|TOK:VOW|Vampire|1||EdgarMarkovsCoffinVampireToken|
|Generate|TOK:VOW|Vampire|2||VampireLifelinkToken|
|Generate|TOK:VOW|Wolf|1||WolfToken|
|Generate|TOK:VOW|Wolf|2||RedWolfToken|
|Generate|TOK:VOW|Zombie|1||ZombieToken|
|Generate|TOK:VOW|Zombie|2||StitcherGeralfZombieToken|

View file

@ -1,2 +0,0 @@
#workaround to remove un-wanted ormlite logs, see https://github.com/magefree/mage/issues/8373
LogBackendType.*=ERROR

View file

@ -40,6 +40,7 @@ import mage.util.SubTypes;
import java.util.*;
import java.util.stream.Collectors;
import mage.game.stack.StackObject;
/**
* @author BetaSteward_at_googlemail.com
@ -93,7 +94,6 @@ public class CardView extends SimpleCardView {
protected boolean faceDown;
protected String alternateName;
protected String originalName;
protected boolean isSplitCard;
protected String leftSplitName;
@ -176,8 +176,8 @@ public class CardView extends SimpleCardView {
this.subTypes = new SubTypes(cardView.subTypes);
this.superTypes = cardView.superTypes;
this.color = cardView.color;
this.frameColor = cardView.frameColor;
this.color = cardView.color.copy();
this.frameColor = cardView.frameColor.copy();
this.frameStyle = cardView.frameStyle;
this.manaCostLeftStr = cardView.manaCostLeftStr;
this.manaCostRightStr = cardView.manaCostRightStr;
@ -197,7 +197,6 @@ public class CardView extends SimpleCardView {
this.flipCard = cardView.flipCard;
this.faceDown = cardView.faceDown;
this.alternateName = cardView.alternateName;
this.originalName = cardView.originalName;
this.isSplitCard = cardView.isSplitCard;
this.leftSplitName = cardView.leftSplitName;
@ -239,8 +238,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);
@ -266,14 +265,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());
@ -317,7 +314,7 @@ public class CardView extends SimpleCardView {
} else if (card instanceof Permanent) {
this.power = Integer.toString(card.getPower().getValue());
this.toughness = Integer.toString(card.getToughness().getValue());
this.cardTypes = card.getCardType(game);
this.cardTypes = new ArrayList<>(card.getCardType(game));
this.faceDown = card.isFaceDown(game);
} else {
// this.hideInfo = true;
@ -392,9 +389,9 @@ public class CardView extends SimpleCardView {
this.displayName = card.getName();
this.displayFullName = fullCardName;
if (game == null) {
this.rules = card.getRules();
this.rules = new ArrayList<>(card.getRules());
} else {
this.rules = card.getRules(game);
this.rules = new ArrayList<>(card.getRules(game));
}
this.manaValue = card.getManaValue();
@ -471,10 +468,10 @@ public class CardView extends SimpleCardView {
this.power = Integer.toString(card.getPower().getValue());
this.toughness = Integer.toString(card.getToughness().getValue());
this.cardTypes = card.getCardType(game);
this.subTypes = card.getSubtype(game);
this.cardTypes = new ArrayList<>(card.getCardType(game));
this.subTypes = new SubTypes(card.getSubtype(game));
this.superTypes = card.getSuperType();
this.color = card.getColor(game);
this.color = card.getColor(game).copy();
this.flipCard = card.isFlipCard();
this.faceDown = !showFaceUp;
@ -494,7 +491,7 @@ public class CardView extends SimpleCardView {
}
//
// set code and card number for token copies to get the image
this.rules = card.getRules(game);
this.rules = new ArrayList<>(card.getRules(game));
this.type = ((PermanentToken) card).getToken().getTokenType();
} else {
this.rarity = card.getRarity();
@ -508,13 +505,11 @@ public class CardView extends SimpleCardView {
if (secondSideCard != null) {
this.secondCardFace = new CardView(secondSideCard, game);
this.alternateName = secondCardFace.getName();
this.originalName = card.getName();
}
this.flipCard = card.isFlipCard();
if (card.isFlipCard() && card.getFlipCardName() != null) {
this.alternateName = card.getFlipCardName();
this.originalName = card.getName();
}
if (card instanceof ModalDoubleFacesCard) {
@ -522,7 +517,6 @@ public class CardView extends SimpleCardView {
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card;
this.secondCardFace = new CardView(mdfCard.getRightHalfCard(), game);
this.alternateName = mdfCard.getRightHalfCard().getName();
this.originalName = card.getName();
}
if (card instanceof Spell) {
@ -569,10 +563,27 @@ public class CardView extends SimpleCardView {
this.rules.add("<span color='green'><i>Chosen mode: " + mode.getEffects().getText(mode) + "</i></span>");
}
}
// show target of a spell on the stack
if (!spell.getSpellAbility().getTargets().isEmpty()) {
StackObject stackObjectTarget = null;
for (Target target : spell.getSpellAbility().getTargets()) {
for (UUID targetId : target.getTargets()) {
MageObject mo = game.getObject(targetId);
if (mo instanceof StackObject) {
stackObjectTarget = (StackObject) mo;
}
if (stackObjectTarget != null) {
this.rules.add("<span color='green'><i>Target on stack: " + stackObjectTarget.getIdName());
}
}
}
}
}
// Frame color
this.frameColor = card.getFrameColor(game);
this.frameColor = card.getFrameColor(game).copy();
// Frame style
this.frameStyle = card.getFrameStyle();
@ -598,10 +609,10 @@ public class CardView extends SimpleCardView {
this.toughness = object.getToughness().toString();
this.loyalty = "";
}
this.cardTypes = object.getCardType(game);
this.subTypes = object.getSubtype(game);
this.cardTypes = new ArrayList<>(object.getCardType(game));
this.subTypes = new SubTypes(object.getSubtype(game));
this.superTypes = object.getSuperType();
this.color = object.getColor(game);
this.color = object.getColor(game).copy();
this.manaCostLeftStr = String.join("", object.getManaCostSymbols());
this.manaCostRightStr = "";
this.manaValue = object.getManaCost().manaValue();
@ -610,18 +621,18 @@ public class CardView extends SimpleCardView {
PermanentToken permanentToken = (PermanentToken) object;
this.rarity = Rarity.COMMON;
this.expansionSetCode = permanentToken.getExpansionSetCode();
this.rules = permanentToken.getRules();
this.rules = new ArrayList<>(permanentToken.getRules());
this.type = permanentToken.getToken().getTokenType();
} else if (object instanceof Emblem) {
this.mageObjectType = MageObjectType.EMBLEM;
Emblem emblem = (Emblem) object;
this.rarity = Rarity.SPECIAL;
this.rules = emblem.getAbilities().getRules(emblem.getName());
this.rules = new ArrayList<>(emblem.getAbilities().getRules(emblem.getName()));
} else if (object instanceof Dungeon) {
this.mageObjectType = MageObjectType.DUNGEON;
Dungeon dungeon = (Dungeon) object;
this.rarity = Rarity.SPECIAL;
this.rules = dungeon.getRules();
this.rules = new ArrayList<>(dungeon.getRules());
} else if (object instanceof Plane) {
this.mageObjectType = MageObjectType.PLANE;
Plane plane = (Plane) object;
@ -629,14 +640,14 @@ public class CardView extends SimpleCardView {
this.frameStyle = FrameStyle.M15_NORMAL;
// Display in landscape/rotated/on its side
this.rotate = true;
this.rules = plane.getAbilities().getRules(plane.getName());
this.rules = new ArrayList<>(plane.getAbilities().getRules(plane.getName()));
} else if (object instanceof Designation) {
this.mageObjectType = MageObjectType.DESIGNATION;
Designation designation = (Designation) object;
this.rarity = Rarity.SPECIAL;
this.frameStyle = FrameStyle.M15_NORMAL;
// Display in landscape/rotated/on its side
this.rules = designation.getAbilities().getRules(designation.getName());
this.rules = new ArrayList<>(designation.getAbilities().getRules(designation.getName()));
}
if (this.rarity == null && object instanceof StackAbility) {
StackAbility stackAbility = (StackAbility) object;
@ -648,7 +659,7 @@ public class CardView extends SimpleCardView {
}
}
// Frame color
this.frameColor = object.getFrameColor(game);
this.frameColor = object.getFrameColor(game).copy();
// Frame style
this.frameStyle = object.getFrameStyle();
// Starting loyalty. Must be extracted from an ability
@ -667,7 +678,7 @@ public class CardView extends SimpleCardView {
this.name = emblem.getName();
this.displayName = name;
this.displayFullName = name;
this.rules = emblem.getRules();
this.rules = new ArrayList<>(emblem.getRules());
// emblem images are always with common (black) symbol
this.frameStyle = FrameStyle.M15_NORMAL;
this.expansionSetCode = emblem.getExpansionSetCode();
@ -682,7 +693,7 @@ public class CardView extends SimpleCardView {
this.name = dungeon.getName();
this.displayName = name;
this.displayFullName = name;
this.rules = dungeon.getRules();
this.rules = new ArrayList<>(dungeon.getRules());
// emblem images are always with common (black) symbol
this.frameStyle = FrameStyle.M15_NORMAL;
this.expansionSetCode = dungeon.getExpansionSetCode();
@ -697,7 +708,7 @@ public class CardView extends SimpleCardView {
this.name = plane.getName();
this.displayName = name;
this.displayFullName = name;
this.rules = plane.getRules();
this.rules = new ArrayList<>(plane.getRules());
// Display the plane in landscape (similar to Fused cards)
this.rotate = true;
this.frameStyle = FrameStyle.M15_NORMAL;
@ -781,16 +792,16 @@ public class CardView extends SimpleCardView {
this.name = token.getName();
this.displayName = token.getName();
this.displayFullName = token.getName();
this.rules = token.getAbilities().getRules(this.name);
this.rules = new ArrayList<>(token.getAbilities().getRules(this.name));
this.power = token.getPower().toString();
this.toughness = token.getToughness().toString();
this.loyalty = "";
this.startingLoyalty = "";
this.cardTypes = token.getCardType(game);
this.subTypes = token.getSubtype(game);
this.cardTypes = new ArrayList<>(token.getCardType(game));
this.subTypes = new SubTypes(token.getSubtype(game));
this.superTypes = token.getSuperType();
this.color = token.getColor(game);
this.frameColor = token.getFrameColor(game);
this.color = token.getColor(game).copy();
this.frameColor = token.getFrameColor(game).copy();
this.frameStyle = token.getFrameStyle();
this.manaCostLeftStr = String.join("", token.getManaCostSymbols());
this.manaCostRightStr = "";
@ -844,7 +855,7 @@ public class CardView extends SimpleCardView {
}
public void overrideRules(List<String> rules) {
this.rules = rules;
this.rules = new ArrayList<>(rules);
}
public void setIsAbility(boolean isAbility) {
@ -998,7 +1009,8 @@ public class CardView extends SimpleCardView {
}
/**
* Name of the other side (transform), flipped, modal double faces card or copying card name.
* Name of the other side (transform), flipped, modal double faces card or
* copying card name.
*
* @return name
*/
@ -1006,24 +1018,10 @@ public class CardView extends SimpleCardView {
return alternateName;
}
/**
* Stores the name of the original name, to provide it for a flipped or
* transformed or copying card
*
* @return
*/
public String getOriginalName() {
return originalName;
}
public void setAlternateName(String alternateName) {
this.alternateName = alternateName;
}
public void setOriginalName(String originalName) {
this.originalName = originalName;
}
public String getLeftSplitName() {
return leftSplitName;
}

View file

@ -26,7 +26,7 @@ public class PermanentView extends CardView {
private final boolean summoningSickness;
private final int damage;
private List<UUID> attachments;
private final CardView original;
private final CardView original; // original card before transforms and modifications
private final boolean copy;
private final String nameOwner; // only filled if != controller
private final boolean controlled;
@ -52,14 +52,14 @@ public class PermanentView extends CardView {
}
this.attachedTo = permanent.getAttachedTo();
if (isToken()) {
original = new CardView(((PermanentToken) permanent).getToken(), game);
original = new CardView(((PermanentToken) permanent).getToken().copy(), (Game) null);
original.expansionSetCode = permanent.getExpansionSetCode();
tokenSetCode = original.getTokenSetCode();
tokenDescriptor = original.getTokenDescriptor();
} else {
if (card != null) {
// original may not be face down
original = new CardView(card, game);
original = new CardView(card.copy(), (Game) null);
} else {
original = null;
}
@ -71,12 +71,10 @@ public class PermanentView extends CardView {
if (original != null && !original.getName().equals(this.getName())) {
if (permanent.isCopy() && permanent.isFlipCard()) {
this.alternateName = permanent.getFlipCardName();
this.originalName = this.getName();
} else {
if (controlled // controller may always know
|| (!morphed && !manifested)) { // others don't know for morph or transformed cards
this.alternateName = original.getName();
this.originalName = this.getName();
}
}
}

View file

@ -21,6 +21,8 @@ import mage.util.GameLog;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.game.stack.StackObject;
import mage.target.Target;
/**
* @author BetaSteward_at_googlemail.com
@ -140,6 +142,24 @@ public class StackAbilityView extends CardView {
HintUtils.appendHints(rules, abilityHints);
}
}
// show target of an ability on the stack if "related objects" is empty
if (!ability.getTargets().isEmpty()
&& names.isEmpty()) {
StackObject stackObjectTarget = null;
for (Target target : ability.getTargets()) {
for (UUID targetId : target.getTargets()) {
MageObject mo = game.getObject(targetId);
if (mo instanceof StackObject) {
stackObjectTarget = (StackObject) mo;
}
if (stackObjectTarget != null) {
this.rules.add("<span color='green'><i>Targeted ability related to this card: " + game.getCard(stackObjectTarget.getSourceId()).getIdName());
}
}
}
}
}
public CardView getSourceCard() {

View file

@ -1,2 +0,0 @@
#workaround to remove un-wanted ormlite logs, see https://github.com/magefree/mage/issues/8373
LogBackendType.*=ERROR

View file

@ -20,6 +20,7 @@ public class AusHighlander extends Constructed {
pointMap.put("Ancestral Recall", 5);
pointMap.put("Time Walk", 5);
pointMap.put("Black Lotus", 4);
pointMap.put("Thassa's Oracle", 4);
pointMap.put("Time Vault", 4);
pointMap.put("Demonic Tutor", 3);
pointMap.put("Mana Crypt", 3);
@ -29,7 +30,6 @@ public class AusHighlander extends Constructed {
pointMap.put("Mox Ruby", 3);
pointMap.put("Mox Sapphire", 3);
pointMap.put("Sol Ring", 3);
pointMap.put("Thassa's Oracle", 3);
pointMap.put("Underworld Breach", 3);
pointMap.put("Vampiric Tutor", 3);
pointMap.put("Channel", 2);
@ -61,6 +61,7 @@ public class AusHighlander extends Constructed {
pointMap.put("Library of Alexandria", 1);
pointMap.put("Life from the Loam", 1);
pointMap.put("Lim-Dul's Vault", 1);
pointMap.put("Lurrus of the Dream-Den", 1);
pointMap.put("Lutri, the Spellchaser", 1);
pointMap.put("Mana Drain", 1);
pointMap.put("Mana Vault", 1);
@ -70,6 +71,7 @@ public class AusHighlander extends Constructed {
pointMap.put("Natural Order", 1);
pointMap.put("Oath of Druids", 1);
pointMap.put("Personal Tutor", 1);
pointMap.put("Profane Tutor", 1);
pointMap.put("Sensei's Divining Top", 1);
pointMap.put("Skullclamp", 1);
pointMap.put("Snapcaster Mage", 1);
@ -80,6 +82,7 @@ public class AusHighlander extends Constructed {
pointMap.put("Tolarian Academy", 1);
pointMap.put("Umezawa's Jitte", 1);
pointMap.put("Uro, Titan of Nature's Wrath", 1);
pointMap.put("Urza's Saga", 1);
pointMap.put("Wasteland", 1);
pointMap.put("Wishclaw Talisman", 1);
pointMap.put("Wrenn and Six", 1);

View file

@ -49,7 +49,7 @@ public class CanadianHighlander extends Constructed {
pointMap.put("Summoner's Pact", 1);
pointMap.put("Survival of the Fittest", 2);
pointMap.put("Tainted Pact", 1);
pointMap.put("Thassa's Oracle", 2);
pointMap.put("Thassa's Oracle", 7);
pointMap.put("Time Vault", 7);
pointMap.put("Time Walk", 7);
pointMap.put("Tinker", 3);

View file

@ -1,9 +1,11 @@
package mage.deck;
import mage.MageObject;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.CanBeYourCommanderAbility;
import mage.abilities.keyword.CompanionAbility;
import mage.abilities.keyword.FriendsForeverAbility;
import mage.abilities.keyword.PartnerAbility;
import mage.abilities.keyword.PartnerWithAbility;
import mage.cards.Card;
@ -14,6 +16,7 @@ import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidatorErrorType;
import mage.constants.CardType;
import mage.filter.FilterMana;
import mage.util.CardUtil;
import mage.util.ManaUtil;
import java.util.*;
@ -40,7 +43,9 @@ public class Commander extends Constructed {
banned.add("Black Lotus");
banned.add("Braids, Cabal Minion");
banned.add("Channel");
banned.add("Cleanse");
banned.add("Coalition Victory");
banned.add("Crusade");
banned.add("Emrakul, the Aeons Torn");
banned.add("Erayo, Soratami Ascendant");
banned.add("Fastbond");
@ -49,7 +54,10 @@ public class Commander extends Constructed {
banned.add("Golos, Tireless Pilgrim");
banned.add("Griselbrand");
banned.add("Hullbreacher");
banned.add("Imprison");
banned.add("Invoke Prejudice");
banned.add("Iona, Shield of Emeria");
banned.add("Jihad");
banned.add("Karakas");
banned.add("Leovold, Emissary of Trest");
banned.add("Library of Alexandria");
@ -62,10 +70,12 @@ public class Commander extends Constructed {
banned.add("Mox Sapphire");
banned.add("Panoptic Mirror");
banned.add("Paradox Engine");
banned.add("Pradesh Gypsies");
banned.add("Primeval Titan");
banned.add("Prophet of Kruphix");
banned.add("Recurring Nightmare");
banned.add("Rofellos, Llanowar Emissary");
banned.add("Stone-Throwing Devils");
banned.add("Sundering Titan");
banned.add("Sway of the Stars");
banned.add("Sylvan Primordial");
@ -183,6 +193,26 @@ public class Commander extends Constructed {
for (Card commander : commanders) {
commanderNames.add(commander.getName());
}
if (commanders.size() == 2
&& commanders
.stream()
.map(MageObject::getAbilities)
.filter(abilities -> abilities.contains(PartnerAbility.getInstance()))
.count() != 2
&& commanders
.stream()
.map(MageObject::getAbilities)
.filter(abilities -> abilities.contains(FriendsForeverAbility.getInstance()))
.count() != 2
&& !CardUtil
.castStream(commanders.stream().map(MageObject::getAbilities), PartnerWithAbility.class)
.map(PartnerWithAbility::getPartnerName)
.allMatch(commanderNames::contains)) {
for (Card commander : commanders) {
addError(DeckValidatorErrorType.PRIMARY, commander.getName(), "Commander with invalid Partner (" + commander.getName() + ')', true);
valid = false;
}
}
for (Card commander : commanders) {
if (bannedCommander.contains(commander.getName())) {
addError(DeckValidatorErrorType.PRIMARY, commander.getName(), "Commander banned (" + commander.getName() + ')', true);
@ -193,24 +223,9 @@ public class Commander extends Constructed {
addError(DeckValidatorErrorType.PRIMARY, commander.getName(), "Commander invalid (" + commander.getName() + ')', true);
valid = false;
}
if (commanders.size() == 2) {
if (commander.getAbilities().contains(PartnerAbility.getInstance())) {
if (bannedPartner.contains(commander.getName())) {
addError(DeckValidatorErrorType.PRIMARY, commander.getName(), "Commander Partner banned (" + commander.getName() + ')', true);
valid = false;
}
} else {
boolean partnersWith = commander.getAbilities()
.stream()
.filter(PartnerWithAbility.class::isInstance)
.map(PartnerWithAbility.class::cast)
.map(PartnerWithAbility::getPartnerName)
.anyMatch(commanderNames::contains);
if (!partnersWith) {
addError(DeckValidatorErrorType.PRIMARY, commander.getName(), "Commander without Partner (" + commander.getName() + ')', true);
valid = false;
}
}
if (commanders.size() == 2 && bannedPartner.contains(commander.getName())) {
addError(DeckValidatorErrorType.PRIMARY, commander.getName(), "Commander Partner banned (" + commander.getName() + ')', true);
valid = false;
}
ManaUtil.collectColorIdentity(colorIdentity, commander.getColorIdentity());
}

View file

@ -15,6 +15,8 @@ public class DuelCommander extends Commander {
banned.add("Cavern of Souls");
banned.add("Channel");
banned.add("Chrome Mox");
banned.add("Cleanse");
banned.add("Crusade");
banned.add("Deflecting Swat");
banned.add("Dig Through Time");
banned.add("Eidolon of the Great Revel");
@ -32,7 +34,10 @@ public class DuelCommander extends Commander {
banned.add("High Tide");
banned.add("Humility");
banned.add("Imperial Seal");
banned.add("Imprison");
banned.add("Invoke Prejudice");
banned.add("Jeweled Lotus");
banned.add("Jihad");
banned.add("Karakas");
banned.add("Library of Alexandria");
banned.add("Lion's Eye Diamond");
@ -55,11 +60,13 @@ public class DuelCommander extends Commander {
banned.add("Necrotic Ooze");
banned.add("Oath of Druids");
banned.add("Polymorph");
banned.add("Pradesh Gypsies");
banned.add("Price of Progress");
banned.add("Protean Hulk");
banned.add("Scapeshift");
banned.add("Sensei's Divining Top");
banned.add("Sol Ring");
banned.add("Stone-Throwing Devils");
banned.add("Strip Mine");
banned.add("Temporal Manipulation");
banned.add("Thassa's Oracle");
@ -92,8 +99,9 @@ public class DuelCommander extends Commander {
bannedCommander.add("Jeska, Thrice Reborn");
bannedCommander.add("Keleth, Sunmane Familiar");
bannedCommander.add("Krark, the Thumbless");
bannedCommander.add("Kraum, Ludevic's Opus");
bannedCommander.add("Livio, Oathsworn Sentinel");
bannedCommander.add("Ludevic, Necro-Alchemist");
bannedCommander.add("Marath, Will of the Wild");
bannedCommander.add("Najeela, the Blade-Blossom");
bannedCommander.add("Oloro, Ageless Ascetic");
bannedCommander.add("Omnath, Locus of Creation");
@ -103,7 +111,6 @@ public class DuelCommander extends Commander {
bannedCommander.add("Rofellos, Llanowar Emissary");
bannedCommander.add("Rograkh, Son of Rohgahh");
bannedCommander.add("Tasigur, the Golden Fang");
bannedCommander.add("Teferi, Temporal Archmage");
bannedCommander.add("Thrasios, Triton Hero");
bannedCommander.add("Tymna, the Weaver");
bannedCommander.add("Urza, Lord High Artificer");

View file

@ -43,6 +43,8 @@ public class EuropeanHighlander extends Constructed {
banned.add("Assemble the Rank and Vile");
banned.add("Backup Plan");
banned.add("Brago's Favor");
banned.add("Cleanse");
banned.add("Crusade");
banned.add("Double Stroke");
banned.add("Echoing Boon");
banned.add("Emissary's Ploy");
@ -50,15 +52,20 @@ public class EuropeanHighlander extends Constructed {
banned.add("Hold the Perimeter");
banned.add("Hymn of the Wilds");
banned.add("Immediate Action");
banned.add("Imprison");
banned.add("Incendiary Dissent");
banned.add("Iterative Analysis");
banned.add("Invoke Prejudice");
banned.add("Jihad");
banned.add("Muzzio's Preparations");
banned.add("Natural Unity");
banned.add("Power Play");
banned.add("Pradesh Gypsies");
banned.add("Secrets of Paradise");
banned.add("Secret Summoning");
banned.add("Sentinel Dispatch");
banned.add("Sovereign's Realm");
banned.add("Stone-Throwing Devils");
banned.add("Summoner's Bond");
banned.add("Unexpected Potential");
banned.add("Weight Advantage");

View file

@ -37,6 +37,7 @@ public class Historic extends Constructed {
banned.add("Demonic Tutor");
banned.add("Fires of Invention");
banned.add("Lightning Bolt");
banned.add("Memory Lapse");
banned.add("Natural Order");
banned.add("Nexus of Fate");
banned.add("Oko, Thief of Crowns");
@ -45,6 +46,7 @@ public class Historic extends Constructed {
banned.add("Swords to Plowshares");
banned.add("Teferi, Time Raveler");
banned.add("Thassa's Oracle");
banned.add("Tibalt's Trickery");
banned.add("Time Warp");
banned.add("Uro, Titan of Nature's Wrath");
banned.add("Veil of Summer");

View file

@ -28,7 +28,9 @@ public class Legacy extends Constructed {
banned.add("Bronze Tablet");
banned.add("Channel");
banned.add("Chaos Orb");
banned.add("Cleanse");
banned.add("Contract from Below");
banned.add("Crusade");
banned.add("Darkpact");
banned.add("Deathrite Shaman");
banned.add("Demonic Attorney");
@ -48,8 +50,11 @@ public class Legacy extends Constructed {
banned.add("Hermit Druid");
banned.add("Immediate Action");
banned.add("Imperial Seal");
banned.add("Imprison");
banned.add("Invoke Prejudice");
banned.add("Iterative Analysis");
banned.add("Jeweled Bird");
banned.add("Jihad");
banned.add("Library of Alexandria");
banned.add("Lurrus of the Dream-Den");
banned.add("Mana Crypt");
@ -71,6 +76,8 @@ public class Legacy extends Constructed {
banned.add("Oath of Druids");
banned.add("Oko, Thief of Crowns");
banned.add("Power Play");
banned.add("Pradesh Gypsies");
banned.add("Ragavan, Nimble Pilferer");
banned.add("Rebirth");
banned.add("Secret Summoning");
banned.add("Secrets of Paradise");
@ -79,6 +86,7 @@ public class Legacy extends Constructed {
banned.add("Shahrazad");
banned.add("Skullclamp");
banned.add("Sol Ring");
banned.add("Stone-Throwing Devils");
banned.add("Strip Mine");
banned.add("Survival of the Fittest");
banned.add("Tempest Efreet");

View file

@ -22,22 +22,28 @@ public class Pauper extends Constructed {
rarities.add(Rarity.LAND);
banned.add("Arcum's Astrolabe");
banned.add("Atog");
banned.add("Bonder's Ornament");
banned.add("Chatterstorm");
banned.add("Cloud of Faeries");
banned.add("Cloudpost");
banned.add("Cranial Plating");
banned.add("Daze");
banned.add("Empty the Warrens");
banned.add("Expedition Map");
banned.add("Fall from Favor");
banned.add("Frantic Search");
banned.add("Gitaxian Probe");
banned.add("Grapeshot");
banned.add("Gush");
banned.add("Hight Tide");
banned.add("High Tide");
banned.add("Hymn to Tourach");
banned.add("Invigorate");
banned.add("Mystic Sanctuary");
banned.add("Peregrine Drake");
banned.add("Prophetic Prism");
banned.add("Sinkhole");
banned.add("Sojourner's Companion");
banned.add("Temporal Fissure");
banned.add("Treasure Cruise");
}

View file

@ -21,6 +21,9 @@ public class Standard extends Constructed {
setCodes.addAll(makeLegalSets());
banned.add("Alrund's Epiphany");
banned.add("Divide by Zero");
banned.add("Faceless Haven");
banned.add("Omnath, Locus of Creation");
}

View file

@ -22,21 +22,28 @@ public class Vintage extends Constructed {
banned.add("Brago's Favor");
banned.add("Bronze Tablet");
banned.add("Chaos Orb");
banned.add("Cleanse");
banned.add("Crusade");
banned.add("Contract from Below");
banned.add("Darkpact");
banned.add("Demonic Attorney");
banned.add("Double Stroke");
banned.add("Falling Star");
banned.add("Immediate Action");
banned.add("Imprison");
banned.add("Invoke Prejudice");
banned.add("Iterative Analysis");
banned.add("Jeweled Bird");
banned.add("Jihad");
banned.add("Muzzio's Preparations");
banned.add("Power Play");
banned.add("Pradesh Gypsies");
banned.add("Rebirth");
banned.add("Secret Summoning");
banned.add("Secrets of Paradise");
banned.add("Sentinel Dispatch");
banned.add("Shahrazad");
banned.add("Stone-Throwing Devils");
banned.add("Tempest Efreet");
banned.add("Timmerian Fiends");
banned.add("Unexpected Potential");

View file

@ -976,24 +976,8 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
if (target.getOriginalTarget() instanceof TargetDefender) {
// TODO: Improve, now planeswalker is always chosen if it exits
List<Permanent> targets;
targets = game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, randomOpponentId, game);
if (targets != null && !targets.isEmpty()) {
for (Permanent planeswalker : targets) {
if (target.canTarget(abilityControllerId, planeswalker.getId(), source, game)) {
target.addTarget(planeswalker.getId(), source, game);
}
if (target.isChosen()) {
return true;
}
}
}
if (!target.isChosen()) {
if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) {
target.addTarget(randomOpponentId, source, game);
}
}
UUID randomDefender = RandomUtil.randomFromCollection(possibleTargets);
target.addTarget(randomDefender, source, game);
return target.isChosen();
}
@ -1668,7 +1652,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
}
// pay phyrexian life costs
if (cost instanceof PhyrexianManaCost) {
if (cost.isPhyrexian()) {
return cost.pay(ability, game, ability, playerId, false, null) || approvingObject != null;
}
@ -2997,21 +2981,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
* @return
*/
private UUID getRandomOpponent(UUID abilityControllerId, Game game) {
UUID randomOpponentId = null;
Set<UUID> opponents = game.getOpponents(abilityControllerId);
if (opponents.size() > 1) {
int rand = RandomUtil.nextInt(opponents.size());
int count = 0;
for (UUID currentId : opponents) {
if (count == rand) {
randomOpponentId = currentId;
break;
}
}
} else if (opponents.size() == 1) {
randomOpponentId = game.getOpponents(abilityControllerId).iterator().next();
}
return randomOpponentId;
return RandomUtil.randomFromCollection(game.getOpponents(abilityControllerId));
}
@Override

View file

@ -223,7 +223,7 @@ public class HumanPlayer extends PlayerImpl {
}
}
// game recived immidiate response on OTHER player concede -- need to process end game and continue to wait
// game recived immediately response on OTHER player concede -- need to process end game and continue to wait
if (response.getResponseConcedeCheck()) {
((GameImpl) game).checkConcede();
if (game.hasEnded()) {
@ -1737,7 +1737,6 @@ public class HumanPlayer extends PlayerImpl {
return true;
} else {
TargetDefender target = new TargetDefender(possibleDefender, attackerId);
target.setNotTarget(true); // player or planswalker hexproof does not prevent attacking a player
if (forcedToAttack) {
StringBuilder sb = new StringBuilder(target.getTargetName());
Permanent attacker = game.getPermanent(attackerId);
@ -1757,7 +1756,6 @@ public class HumanPlayer extends PlayerImpl {
protected UUID selectDefenderForAllAttack(Set<UUID> defenders, Game game) {
TargetDefender target = new TargetDefender(defenders, null);
target.setNotTarget(true); // player or planswalker hexproof does not prevent attacking a player
if (chooseTarget(Outcome.Damage, target, null, game)) {
return getFixedResponseUUID(game);
}

View file

@ -11,7 +11,7 @@ import mage.game.draft.DraftCube;
public class MonoBlueCube extends DraftCube {
public MonoBlueCube() {
super("Mono Blue Cube");
super("elmikkino's Mono Blue Cube");
cubeCards.add(new CardIdentity("Academy Ruins", ""));
cubeCards.add(new CardIdentity("Adaptive Automaton", ""));

View file

@ -0,0 +1,552 @@
package mage.tournament.cubes;
import mage.game.draft.DraftCube;
public class VintageCubeFebruary2022 extends DraftCube {
public VintageCubeFebruary2022() {
super("MTGO Vintage Cube Februrary 2022");
cubeCards.add(new CardIdentity("Abbot of Keral Keep", ""));
cubeCards.add(new CardIdentity("Abrade", ""));
cubeCards.add(new CardIdentity("Academy Ruins", ""));
cubeCards.add(new CardIdentity("Acidic Slime", ""));
cubeCards.add(new CardIdentity("Adanto Vanguard", ""));
cubeCards.add(new CardIdentity("Adeline, Resplendent Cathar", ""));
cubeCards.add(new CardIdentity("Ancestral Recall", ""));
cubeCards.add(new CardIdentity("Ancestral Vision", ""));
cubeCards.add(new CardIdentity("Ancient Tomb", ""));
cubeCards.add(new CardIdentity("Animate Dead", ""));
cubeCards.add(new CardIdentity("Arbor Elf", ""));
cubeCards.add(new CardIdentity("Archangel Avacyn", ""));
cubeCards.add(new CardIdentity("Archon of Cruelty", ""));
cubeCards.add(new CardIdentity("Archon of Valor's Reach", ""));
cubeCards.add(new CardIdentity("Arid Mesa", ""));
cubeCards.add(new CardIdentity("Armageddon", ""));
cubeCards.add(new CardIdentity("Ashen Rider", ""));
cubeCards.add(new CardIdentity("Ashiok, Nightmare Weaver", ""));
cubeCards.add(new CardIdentity("Assassin's Trophy", ""));
cubeCards.add(new CardIdentity("Augur of Autumn", ""));
cubeCards.add(new CardIdentity("Avalanche Riders", ""));
cubeCards.add(new CardIdentity("Avenger of Zendikar", ""));
cubeCards.add(new CardIdentity("Azorius Signet", ""));
cubeCards.add(new CardIdentity("Badlands", ""));
cubeCards.add(new CardIdentity("Balance", ""));
cubeCards.add(new CardIdentity("Baleful Strix", ""));
cubeCards.add(new CardIdentity("Baneslayer Angel", ""));
cubeCards.add(new CardIdentity("Banishing Light", ""));
cubeCards.add(new CardIdentity("Baral, Chief of Compliance", ""));
cubeCards.add(new CardIdentity("Basalt Monolith", ""));
cubeCards.add(new CardIdentity("Batterskull", ""));
cubeCards.add(new CardIdentity("Bayou", ""));
cubeCards.add(new CardIdentity("Bazaar of Baghdad", ""));
cubeCards.add(new CardIdentity("Birds of Paradise", ""));
cubeCards.add(new CardIdentity("Birgi, God of Storytelling", ""));
cubeCards.add(new CardIdentity("Birthing Pod", ""));
cubeCards.add(new CardIdentity("Bitterblossom", ""));
cubeCards.add(new CardIdentity("Black Lotus", ""));
cubeCards.add(new CardIdentity("Blackcleave Cliffs", ""));
cubeCards.add(new CardIdentity("Blade Splicer", ""));
cubeCards.add(new CardIdentity("Blightsteel Colossus", ""));
cubeCards.add(new CardIdentity("Blood Crypt", ""));
cubeCards.add(new CardIdentity("Bloodchief's Thirst", ""));
cubeCards.add(new CardIdentity("Bloodghast", ""));
cubeCards.add(new CardIdentity("Bloodstained Mire", ""));
cubeCards.add(new CardIdentity("Bloodthirsty Adversary", ""));
cubeCards.add(new CardIdentity("Bloodtithe Harvester", ""));
cubeCards.add(new CardIdentity("Blooming Marsh", ""));
cubeCards.add(new CardIdentity("Bolas's Citadel", ""));
cubeCards.add(new CardIdentity("Bomat Courier", ""));
cubeCards.add(new CardIdentity("Bone Shredder", ""));
cubeCards.add(new CardIdentity("Bonecrusher Giant", ""));
cubeCards.add(new CardIdentity("Boros Signet", ""));
cubeCards.add(new CardIdentity("Boseiju, Who Endures", ""));
cubeCards.add(new CardIdentity("Botanical Sanctum", ""));
cubeCards.add(new CardIdentity("Braids, Cabal Minion", ""));
cubeCards.add(new CardIdentity("Brain Freeze", ""));
cubeCards.add(new CardIdentity("Brainstorm", ""));
cubeCards.add(new CardIdentity("Brazen Borrower", ""));
cubeCards.add(new CardIdentity("Breeding Pool", ""));
cubeCards.add(new CardIdentity("Bribery", ""));
cubeCards.add(new CardIdentity("Burst Lightning", ""));
cubeCards.add(new CardIdentity("Cabal Ritual", ""));
cubeCards.add(new CardIdentity("Cathar Commando", ""));
cubeCards.add(new CardIdentity("Celestial Colonnade", ""));
cubeCards.add(new CardIdentity("Chain Lightning", ""));
cubeCards.add(new CardIdentity("Chandra, Torch of Defiance", ""));
cubeCards.add(new CardIdentity("Channel", ""));
cubeCards.add(new CardIdentity("Char", ""));
cubeCards.add(new CardIdentity("Chart a Course", ""));
cubeCards.add(new CardIdentity("Chrome Mox", ""));
cubeCards.add(new CardIdentity("Circle of Dreams Druid", ""));
cubeCards.add(new CardIdentity("Coalition Relic", ""));
cubeCards.add(new CardIdentity("Coercive Portal", ""));
cubeCards.add(new CardIdentity("Collective Brutality", ""));
cubeCards.add(new CardIdentity("Concealed Courtyard", ""));
cubeCards.add(new CardIdentity("Condemn", ""));
cubeCards.add(new CardIdentity("Consecrated Sphinx", ""));
cubeCards.add(new CardIdentity("Containment Priest", ""));
cubeCards.add(new CardIdentity("Copperline Gorge", ""));
cubeCards.add(new CardIdentity("Council's Judgment", ""));
cubeCards.add(new CardIdentity("Counterspell", ""));
cubeCards.add(new CardIdentity("Courser of Kruphix", ""));
cubeCards.add(new CardIdentity("Craterhoof Behemoth", ""));
cubeCards.add(new CardIdentity("Creeping Tar Pit", ""));
cubeCards.add(new CardIdentity("Crop Rotation", ""));
cubeCards.add(new CardIdentity("Crucible of Worlds", ""));
cubeCards.add(new CardIdentity("Cryptbreaker", ""));
cubeCards.add(new CardIdentity("Cryptic Command", ""));
cubeCards.add(new CardIdentity("Cultivate", ""));
cubeCards.add(new CardIdentity("Custodi Lich", ""));
cubeCards.add(new CardIdentity("Dack Fayden", ""));
cubeCards.add(new CardIdentity("Damn", ""));
cubeCards.add(new CardIdentity("Damnation", ""));
cubeCards.add(new CardIdentity("Daretti, Ingenious Iconoclast", ""));
cubeCards.add(new CardIdentity("Daretti, Scrap Savant", ""));
cubeCards.add(new CardIdentity("Dark Confidant", ""));
cubeCards.add(new CardIdentity("Dark Depths", ""));
cubeCards.add(new CardIdentity("Dark Ritual", ""));
cubeCards.add(new CardIdentity("Darkslick Shores", ""));
cubeCards.add(new CardIdentity("Dauthi Voidwalker", ""));
cubeCards.add(new CardIdentity("Daze", ""));
cubeCards.add(new CardIdentity("Deathcap Glade", ""));
cubeCards.add(new CardIdentity("Deceiver Exarch", ""));
cubeCards.add(new CardIdentity("Demonic Tutor", ""));
cubeCards.add(new CardIdentity("Deranged Hermit", ""));
cubeCards.add(new CardIdentity("Deserted Beach", ""));
cubeCards.add(new CardIdentity("Desperate Ritual", ""));
cubeCards.add(new CardIdentity("Destructive Force", ""));
cubeCards.add(new CardIdentity("Devoted Druid", ""));
cubeCards.add(new CardIdentity("Dig Through Time", ""));
cubeCards.add(new CardIdentity("Dimir Signet", ""));
cubeCards.add(new CardIdentity("Dire Fleet Daredevil", ""));
cubeCards.add(new CardIdentity("Disenchant", ""));
cubeCards.add(new CardIdentity("Dismember", ""));
cubeCards.add(new CardIdentity("Dockside Extortionist", ""));
cubeCards.add(new CardIdentity("Dragon's Rage Channeler", ""));
cubeCards.add(new CardIdentity("Dreamroot Cascade", ""));
cubeCards.add(new CardIdentity("Duress", ""));
cubeCards.add(new CardIdentity("Eater of Virtue", ""));
cubeCards.add(new CardIdentity("Echo of Eons", ""));
cubeCards.add(new CardIdentity("Edric, Spymaster of Trest", ""));
cubeCards.add(new CardIdentity("Eidolon of the Great Revel", ""));
cubeCards.add(new CardIdentity("Elder Gargaroth", ""));
cubeCards.add(new CardIdentity("Elesh Norn, Grand Cenobite", ""));
cubeCards.add(new CardIdentity("Elite Spellbinder", ""));
cubeCards.add(new CardIdentity("Elspeth Conquers Death", ""));
cubeCards.add(new CardIdentity("Elspeth, Knight-Errant", ""));
cubeCards.add(new CardIdentity("Elspeth, Sun's Champion", ""));
cubeCards.add(new CardIdentity("Elvish Mystic", ""));
cubeCards.add(new CardIdentity("Elvish Reclaimer", ""));
cubeCards.add(new CardIdentity("Embereth Shieldbreaker", ""));
cubeCards.add(new CardIdentity("Empty the Warrens", ""));
cubeCards.add(new CardIdentity("Emrakul, the Aeons Torn", ""));
cubeCards.add(new CardIdentity("Emrakul, the Promised End", ""));
cubeCards.add(new CardIdentity("Emry, Lurker of the Loch", ""));
cubeCards.add(new CardIdentity("Endurance", ""));
cubeCards.add(new CardIdentity("Entomb", ""));
cubeCards.add(new CardIdentity("Ephemerate", ""));
cubeCards.add(new CardIdentity("Escape to the Wilds", ""));
cubeCards.add(new CardIdentity("Esper Sentinel", ""));
cubeCards.add(new CardIdentity("Eternal Witness", ""));
cubeCards.add(new CardIdentity("Eureka", ""));
cubeCards.add(new CardIdentity("Everflowing Chalice", ""));
cubeCards.add(new CardIdentity("Exhume", ""));
cubeCards.add(new CardIdentity("Expansion // Explosion", ""));
cubeCards.add(new CardIdentity("Expressive Iteration", ""));
cubeCards.add(new CardIdentity("Fable of the Mirror-Breaker", ""));
cubeCards.add(new CardIdentity("Fact or Fiction", ""));
cubeCards.add(new CardIdentity("Faithless Looting", ""));
cubeCards.add(new CardIdentity("Fallen Shinobi", ""));
cubeCards.add(new CardIdentity("Fastbond", ""));
cubeCards.add(new CardIdentity("Fatal Push", ""));
cubeCards.add(new CardIdentity("Fauna Shaman", ""));
cubeCards.add(new CardIdentity("Field of the Dead", ""));
cubeCards.add(new CardIdentity("Fiery Islet", ""));
cubeCards.add(new CardIdentity("Figure of Destiny", ""));
cubeCards.add(new CardIdentity("Finale of Devastation", ""));
cubeCards.add(new CardIdentity("Fireblast", ""));
cubeCards.add(new CardIdentity("Firebolt", ""));
cubeCards.add(new CardIdentity("Flickerwisp", ""));
cubeCards.add(new CardIdentity("Flooded Strand", ""));
cubeCards.add(new CardIdentity("Force of Negation", ""));
cubeCards.add(new CardIdentity("Force of Will", ""));
cubeCards.add(new CardIdentity("Fractured Identity", ""));
cubeCards.add(new CardIdentity("Frantic Search", ""));
cubeCards.add(new CardIdentity("Fury", ""));
cubeCards.add(new CardIdentity("Fyndhorn Elves", ""));
cubeCards.add(new CardIdentity("Gaea's Cradle", ""));
cubeCards.add(new CardIdentity("Garruk Relentless", ""));
cubeCards.add(new CardIdentity("Garruk Wildspeaker", ""));
cubeCards.add(new CardIdentity("Geist of Saint Traft", ""));
cubeCards.add(new CardIdentity("Gideon Blackblade", ""));
cubeCards.add(new CardIdentity("Gideon Jura", ""));
cubeCards.add(new CardIdentity("Gideon, Ally of Zendikar", ""));
cubeCards.add(new CardIdentity("Gilded Drake", ""));
cubeCards.add(new CardIdentity("Gilded Lotus", ""));
cubeCards.add(new CardIdentity("Gitaxian Probe", ""));
cubeCards.add(new CardIdentity("Giver of Runes", ""));
cubeCards.add(new CardIdentity("Glen Elendra Archmage", ""));
cubeCards.add(new CardIdentity("Goblin Bombardment", ""));
cubeCards.add(new CardIdentity("Goblin Electromancer", ""));
cubeCards.add(new CardIdentity("Goblin Guide", ""));
cubeCards.add(new CardIdentity("Goblin Rabblemaster", ""));
cubeCards.add(new CardIdentity("Goblin Welder", ""));
cubeCards.add(new CardIdentity("Godless Shrine", ""));
cubeCards.add(new CardIdentity("Goldspan Dragon", ""));
cubeCards.add(new CardIdentity("Golgari Signet", ""));
cubeCards.add(new CardIdentity("Golos, Tireless Pilgrim", ""));
cubeCards.add(new CardIdentity("Gonti, Lord of Luxury", ""));
cubeCards.add(new CardIdentity("Grave Titan", ""));
cubeCards.add(new CardIdentity("Green Sun's Zenith", ""));
cubeCards.add(new CardIdentity("Grief", ""));
cubeCards.add(new CardIdentity("Grim Lavamancer", ""));
cubeCards.add(new CardIdentity("Grim Monolith", ""));
cubeCards.add(new CardIdentity("Griselbrand", ""));
cubeCards.add(new CardIdentity("Grist, the Hunger Tide", ""));
cubeCards.add(new CardIdentity("Gruul Signet", ""));
cubeCards.add(new CardIdentity("Gush", ""));
cubeCards.add(new CardIdentity("Halana and Alena, Partners", ""));
cubeCards.add(new CardIdentity("Hallowed Fountain", ""));
cubeCards.add(new CardIdentity("Hangarback Walker", ""));
cubeCards.add(new CardIdentity("Haunted Ridge", ""));
cubeCards.add(new CardIdentity("Hazoret the Fervent", ""));
cubeCards.add(new CardIdentity("Heartbeat of Spring", ""));
cubeCards.add(new CardIdentity("Hellrider", ""));
cubeCards.add(new CardIdentity("Hero of Bladehold", ""));
cubeCards.add(new CardIdentity("Hero's Downfall", ""));
cubeCards.add(new CardIdentity("Hexdrinker", ""));
cubeCards.add(new CardIdentity("High Tide", ""));
cubeCards.add(new CardIdentity("Horizon Canopy", ""));
cubeCards.add(new CardIdentity("Hornet Queen", ""));
cubeCards.add(new CardIdentity("Hullbreaker Horror", ""));
cubeCards.add(new CardIdentity("Huntmaster of the Fells", ""));
cubeCards.add(new CardIdentity("Hydroid Krasis", ""));
cubeCards.add(new CardIdentity("Hymn to Tourach", ""));
cubeCards.add(new CardIdentity("Ignoble Hierarch", ""));
cubeCards.add(new CardIdentity("Imperial Recruiter", ""));
cubeCards.add(new CardIdentity("Imperial Seal", ""));
cubeCards.add(new CardIdentity("Incinerate", ""));
cubeCards.add(new CardIdentity("Infernal Grasp", ""));
cubeCards.add(new CardIdentity("Inferno Titan", ""));
cubeCards.add(new CardIdentity("Inkwell Leviathan", ""));
cubeCards.add(new CardIdentity("Inquisition of Kozilek", ""));
cubeCards.add(new CardIdentity("Inspiring Vantage", ""));
cubeCards.add(new CardIdentity("Intrepid Adversary", ""));
cubeCards.add(new CardIdentity("Iona, Shield of Emeria", ""));
cubeCards.add(new CardIdentity("Izzet Signet", ""));
cubeCards.add(new CardIdentity("Jace, the Mind Sculptor", ""));
cubeCards.add(new CardIdentity("Jace, Vryn's Prodigy", ""));
cubeCards.add(new CardIdentity("Jin-Gitaxias, Progress Tyrant", ""));
cubeCards.add(new CardIdentity("Jokulhaups", ""));
cubeCards.add(new CardIdentity("Joraga Treespeaker", ""));
cubeCards.add(new CardIdentity("Karakas", ""));
cubeCards.add(new CardIdentity("Karmic Guide", ""));
cubeCards.add(new CardIdentity("Karn Liberated", ""));
cubeCards.add(new CardIdentity("Karn, Scion of Urza", ""));
cubeCards.add(new CardIdentity("Kiki-Jiki, Mirror Breaker", ""));
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("Kogla, the Titan Ape", ""));
cubeCards.add(new CardIdentity("Kolaghan's Command", ""));
cubeCards.add(new CardIdentity("Koth of the Hammer", ""));
cubeCards.add(new CardIdentity("Kroxa, Titan of Death's Hunger", ""));
cubeCards.add(new CardIdentity("Kuldotha Forgemaster", ""));
cubeCards.add(new CardIdentity("Laelia, the Blade Reforged", ""));
cubeCards.add(new CardIdentity("Land Tax", ""));
cubeCards.add(new CardIdentity("Lavaclaw Reaches", ""));
cubeCards.add(new CardIdentity("Leonin Relic-Warder", ""));
cubeCards.add(new CardIdentity("Leovold, Emissary of Trest", ""));
cubeCards.add(new CardIdentity("Library of Alexandria", ""));
cubeCards.add(new CardIdentity("Life from the Loam", ""));
cubeCards.add(new CardIdentity("Light Up the Stage", ""));
cubeCards.add(new CardIdentity("Lightning Bolt", ""));
cubeCards.add(new CardIdentity("Lightning Helix", ""));
cubeCards.add(new CardIdentity("Liliana of the Veil", ""));
cubeCards.add(new CardIdentity("Liliana, the Last Hope", ""));
cubeCards.add(new CardIdentity("Lion Sash", ""));
cubeCards.add(new CardIdentity("Lion's Eye Diamond", ""));
cubeCards.add(new CardIdentity("Living Death", ""));
cubeCards.add(new CardIdentity("Llanowar Elves", ""));
cubeCards.add(new CardIdentity("Lodestone Golem", ""));
cubeCards.add(new CardIdentity("Lotus Bloom", ""));
cubeCards.add(new CardIdentity("Lotus Petal", ""));
cubeCards.add(new CardIdentity("Lurrus of the Dream-Den", ""));
cubeCards.add(new CardIdentity("Lyra Dawnbringer", ""));
cubeCards.add(new CardIdentity("Maelstrom Pulse", ""));
cubeCards.add(new CardIdentity("Magus of the Order", ""));
cubeCards.add(new CardIdentity("Makeshift Mannequin", ""));
cubeCards.add(new CardIdentity("Mana Crypt", ""));
cubeCards.add(new CardIdentity("Mana Drain", ""));
cubeCards.add(new CardIdentity("Mana Flare", ""));
cubeCards.add(new CardIdentity("Mana Leak", ""));
cubeCards.add(new CardIdentity("Mana Tithe", ""));
cubeCards.add(new CardIdentity("Mana Vault", ""));
cubeCards.add(new CardIdentity("Manamorphose", ""));
cubeCards.add(new CardIdentity("Marsh Flats", ""));
cubeCards.add(new CardIdentity("Massacre Wurm", ""));
cubeCards.add(new CardIdentity("Memory Deluge", ""));
cubeCards.add(new CardIdentity("Memory Jar", ""));
cubeCards.add(new CardIdentity("Mesmeric Fiend", ""));
cubeCards.add(new CardIdentity("Metalworker", ""));
cubeCards.add(new CardIdentity("Mind Twist", ""));
cubeCards.add(new CardIdentity("Mind's Desire", ""));
cubeCards.add(new CardIdentity("Mindslaver", ""));
cubeCards.add(new CardIdentity("Mirari's Wake", ""));
cubeCards.add(new CardIdentity("Miscalculation", ""));
cubeCards.add(new CardIdentity("Mishra's Factory", ""));
cubeCards.add(new CardIdentity("Mishra's Workshop", ""));
cubeCards.add(new CardIdentity("Misty Rainforest", ""));
cubeCards.add(new CardIdentity("Monastery Mentor", ""));
cubeCards.add(new CardIdentity("Monastery Swiftspear", ""));
cubeCards.add(new CardIdentity("Mother of Runes", ""));
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("Mulldrifter", ""));
cubeCards.add(new CardIdentity("Murderous Rider", ""));
cubeCards.add(new CardIdentity("Murktide Regent", ""));
cubeCards.add(new CardIdentity("Mutavault", ""));
cubeCards.add(new CardIdentity("Myr Battlesphere", ""));
cubeCards.add(new CardIdentity("Mystic Confluence", ""));
cubeCards.add(new CardIdentity("Mystical Tutor", ""));
cubeCards.add(new CardIdentity("Nahiri, the Harbinger", ""));
cubeCards.add(new CardIdentity("Narset, Parter of Veils", ""));
cubeCards.add(new CardIdentity("Nashi, Moon Sage's Scion", ""));
cubeCards.add(new CardIdentity("Natural Order", ""));
cubeCards.add(new CardIdentity("Necromancy", ""));
cubeCards.add(new CardIdentity("Night's Whisper", ""));
cubeCards.add(new CardIdentity("Nighthawk Scavenger", ""));
cubeCards.add(new CardIdentity("Nissa, Vastwood Seer", ""));
cubeCards.add(new CardIdentity("Nissa, Who Shakes the World", ""));
cubeCards.add(new CardIdentity("Niv-Mizzet Reborn", ""));
cubeCards.add(new CardIdentity("Noble Hierarch", ""));
cubeCards.add(new CardIdentity("Nurturing Peatland", ""));
cubeCards.add(new CardIdentity("Oath of Druids", ""));
cubeCards.add(new CardIdentity("Oblivion Stone", ""));
cubeCards.add(new CardIdentity("Oko, Thief of Crowns", ""));
cubeCards.add(new CardIdentity("Olivia, Crimson Bride", ""));
cubeCards.add(new CardIdentity("Omnath, Locus of Creation", ""));
cubeCards.add(new CardIdentity("Oona's Prowler", ""));
cubeCards.add(new CardIdentity("Ophiomancer", ""));
cubeCards.add(new CardIdentity("Opposition", ""));
cubeCards.add(new CardIdentity("Oracle of Mul Daya", ""));
cubeCards.add(new CardIdentity("Orzhov Signet", ""));
cubeCards.add(new CardIdentity("Oust", ""));
cubeCards.add(new CardIdentity("Overgrown Farmland", ""));
cubeCards.add(new CardIdentity("Overgrown Tomb", ""));
cubeCards.add(new CardIdentity("Pack Rat", ""));
cubeCards.add(new CardIdentity("Palace Jailer", ""));
cubeCards.add(new CardIdentity("Palinchron", ""));
cubeCards.add(new CardIdentity("Parallax Wave", ""));
cubeCards.add(new CardIdentity("Past in Flames", ""));
cubeCards.add(new CardIdentity("Path to Exile", ""));
cubeCards.add(new CardIdentity("Pest Infestation", ""));
cubeCards.add(new CardIdentity("Pestermite", ""));
cubeCards.add(new CardIdentity("Phantasmal Image", ""));
cubeCards.add(new CardIdentity("Phyrexian Metamorph", ""));
cubeCards.add(new CardIdentity("Phyrexian Revoker", ""));
cubeCards.add(new CardIdentity("Pia and Kiran Nalaar", ""));
cubeCards.add(new CardIdentity("Plateau", ""));
cubeCards.add(new CardIdentity("Plow Under", ""));
cubeCards.add(new CardIdentity("Polluted Delta", ""));
cubeCards.add(new CardIdentity("Polukranos, World Eater", ""));
cubeCards.add(new CardIdentity("Ponder", ""));
cubeCards.add(new CardIdentity("Porcelain Legionnaire", ""));
cubeCards.add(new CardIdentity("Portent", ""));
cubeCards.add(new CardIdentity("Preordain", ""));
cubeCards.add(new CardIdentity("Primeval Titan", ""));
cubeCards.add(new CardIdentity("Prismatic Vista", ""));
cubeCards.add(new CardIdentity("Progenitus", ""));
cubeCards.add(new CardIdentity("Putrid Imp", ""));
cubeCards.add(new CardIdentity("Pyretic Ritual", ""));
cubeCards.add(new CardIdentity("Questing Beast", ""));
cubeCards.add(new CardIdentity("Ragavan, Nimble Pilferer", ""));
cubeCards.add(new CardIdentity("Raging Ravine", ""));
cubeCards.add(new CardIdentity("Rakdos Signet", ""));
cubeCards.add(new CardIdentity("Ramunap Excavator", ""));
cubeCards.add(new CardIdentity("Ravages of War", ""));
cubeCards.add(new CardIdentity("Ravenous Chupacabra", ""));
cubeCards.add(new CardIdentity("Razorverge Thicket", ""));
cubeCards.add(new CardIdentity("Reanimate", ""));
cubeCards.add(new CardIdentity("Reclamation Sage", ""));
cubeCards.add(new CardIdentity("Recruiter of the Guard", ""));
cubeCards.add(new CardIdentity("Recurring Nightmare", ""));
cubeCards.add(new CardIdentity("Red Elemental Blast", ""));
cubeCards.add(new CardIdentity("Regrowth", ""));
cubeCards.add(new CardIdentity("Relic of Progenitus", ""));
cubeCards.add(new CardIdentity("Remand", ""));
cubeCards.add(new CardIdentity("Repeal", ""));
cubeCards.add(new CardIdentity("Restoration Angel", ""));
cubeCards.add(new CardIdentity("Retrofitter Foundry", ""));
cubeCards.add(new CardIdentity("Rishadan Port", ""));
cubeCards.add(new CardIdentity("Rockfall Vale", ""));
cubeCards.add(new CardIdentity("Rofellos, Llanowar Emissary", ""));
cubeCards.add(new CardIdentity("Rotting Regisaur", ""));
cubeCards.add(new CardIdentity("Runaway Steam-Kin", ""));
cubeCards.add(new CardIdentity("Sacred Foundry", ""));
cubeCards.add(new CardIdentity("Sakura-Tribe Elder", ""));
cubeCards.add(new CardIdentity("Satoru Umezawa", ""));
cubeCards.add(new CardIdentity("Savannah", ""));
cubeCards.add(new CardIdentity("Scalding Tarn", ""));
cubeCards.add(new CardIdentity("Scavenging Ooze", ""));
cubeCards.add(new CardIdentity("Scrapheap Scrounger", ""));
cubeCards.add(new CardIdentity("Scrubland", ""));
cubeCards.add(new CardIdentity("Sea Gate Stormcaller", ""));
cubeCards.add(new CardIdentity("Seachrome Coast", ""));
cubeCards.add(new CardIdentity("Seasoned Pyromancer", ""));
cubeCards.add(new CardIdentity("Sedgemoor Witch", ""));
cubeCards.add(new CardIdentity("Seething Song", ""));
cubeCards.add(new CardIdentity("Selesnya Signet", ""));
cubeCards.add(new CardIdentity("Selfless Spirit", ""));
cubeCards.add(new CardIdentity("Sensei's Divining Top", ""));
cubeCards.add(new CardIdentity("Shallow Grave", ""));
cubeCards.add(new CardIdentity("Shark Typhoon", ""));
cubeCards.add(new CardIdentity("Shattered Sanctum", ""));
cubeCards.add(new CardIdentity("Shelldock Isle", ""));
cubeCards.add(new CardIdentity("Shipwreck Marsh", ""));
cubeCards.add(new CardIdentity("Show and Tell", ""));
cubeCards.add(new CardIdentity("Showdown of the Skalds", ""));
cubeCards.add(new CardIdentity("Shriekmaw", ""));
cubeCards.add(new CardIdentity("Silent Clearing", ""));
cubeCards.add(new CardIdentity("Silverblade Paladin", ""));
cubeCards.add(new CardIdentity("Simic Signet", ""));
cubeCards.add(new CardIdentity("Skullclamp", ""));
cubeCards.add(new CardIdentity("Skyclave Apparition", ""));
cubeCards.add(new CardIdentity("Skyclave Shade", ""));
cubeCards.add(new CardIdentity("Smokestack", ""));
cubeCards.add(new CardIdentity("Smuggler's Copter", ""));
cubeCards.add(new CardIdentity("Snapcaster Mage", ""));
cubeCards.add(new CardIdentity("Sneak Attack", ""));
cubeCards.add(new CardIdentity("Snuff Out", ""));
cubeCards.add(new CardIdentity("Sol Ring", ""));
cubeCards.add(new CardIdentity("Solitude", ""));
cubeCards.add(new CardIdentity("Soulfire Grand Master", ""));
cubeCards.add(new CardIdentity("Sower of Temptation", ""));
cubeCards.add(new CardIdentity("Spear of Heliod", ""));
cubeCards.add(new CardIdentity("Spectral Procession", ""));
cubeCards.add(new CardIdentity("Spell Pierce", ""));
cubeCards.add(new CardIdentity("Spellseeker", ""));
cubeCards.add(new CardIdentity("Sphinx of the Steel Wind", ""));
cubeCards.add(new CardIdentity("Spirebluff Canal", ""));
cubeCards.add(new CardIdentity("Spirit-Sister's Call", ""));
cubeCards.add(new CardIdentity("Splinter Twin", ""));
cubeCards.add(new CardIdentity("Steam Vents", ""));
cubeCards.add(new CardIdentity("Stomping Ground", ""));
cubeCards.add(new CardIdentity("Stoneforge Mystic", ""));
cubeCards.add(new CardIdentity("Stormcarved Coast", ""));
cubeCards.add(new CardIdentity("Strip Mine", ""));
cubeCards.add(new CardIdentity("Student of Warfare", ""));
cubeCards.add(new CardIdentity("Sulfuric Vortex", ""));
cubeCards.add(new CardIdentity("Sun Titan", ""));
cubeCards.add(new CardIdentity("Sunbaked Canyon", ""));
cubeCards.add(new CardIdentity("Sundering Titan", ""));
cubeCards.add(new CardIdentity("Sundown Pass", ""));
cubeCards.add(new CardIdentity("Survival of the Fittest", ""));
cubeCards.add(new CardIdentity("Suspicious Stowaway", ""));
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("Swords to Plowshares", ""));
cubeCards.add(new CardIdentity("Sylvan Caryatid", ""));
cubeCards.add(new CardIdentity("Sylvan Library", ""));
cubeCards.add(new CardIdentity("Taiga", ""));
cubeCards.add(new CardIdentity("Tamiyo, Compleated Sage", ""));
cubeCards.add(new CardIdentity("Tangle Wire", ""));
cubeCards.add(new CardIdentity("Teferi, Hero of Dominaria", ""));
cubeCards.add(new CardIdentity("Teferi, Time Raveler", ""));
cubeCards.add(new CardIdentity("Temple Garden", ""));
cubeCards.add(new CardIdentity("Tendrils of Agony", ""));
cubeCards.add(new CardIdentity("Terastodon", ""));
cubeCards.add(new CardIdentity("Tezzeret the Seeker", ""));
cubeCards.add(new CardIdentity("Thalia, Guardian of Thraben", ""));
cubeCards.add(new CardIdentity("Thassa's Oracle", ""));
cubeCards.add(new CardIdentity("The Gitrog Monster", ""));
cubeCards.add(new CardIdentity("The Restoration of Eiganjo", ""));
cubeCards.add(new CardIdentity("The Scarab God", ""));
cubeCards.add(new CardIdentity("The Wandering Emperor", ""));
cubeCards.add(new CardIdentity("Thespian's Stage", ""));
cubeCards.add(new CardIdentity("Thieving Skydiver", ""));
cubeCards.add(new CardIdentity("Thirst for Discovery", ""));
cubeCards.add(new CardIdentity("Thoughtseize", ""));
cubeCards.add(new CardIdentity("Thousand-Year Storm", ""));
cubeCards.add(new CardIdentity("Thraben Inspector", ""));
cubeCards.add(new CardIdentity("Thragtusk", ""));
cubeCards.add(new CardIdentity("Thran Dynamo", ""));
cubeCards.add(new CardIdentity("Through the Breach", ""));
cubeCards.add(new CardIdentity("Thundermaw Hellkite", ""));
cubeCards.add(new CardIdentity("Tidehollow Sculler", ""));
cubeCards.add(new CardIdentity("Time Spiral", ""));
cubeCards.add(new CardIdentity("Time Walk", ""));
cubeCards.add(new CardIdentity("Time Warp", ""));
cubeCards.add(new CardIdentity("Timetwister", ""));
cubeCards.add(new CardIdentity("Tinker", ""));
cubeCards.add(new CardIdentity("Tireless Tracker", ""));
cubeCards.add(new CardIdentity("Tolarian Academy", ""));
cubeCards.add(new CardIdentity("Toski, Bearer of Secrets", ""));
cubeCards.add(new CardIdentity("Tovolar's Huntmaster", ""));
cubeCards.add(new CardIdentity("Toxic Deluge", ""));
cubeCards.add(new CardIdentity("Treachery", ""));
cubeCards.add(new CardIdentity("Treasure Cruise", ""));
cubeCards.add(new CardIdentity("Trinket Mage", ""));
cubeCards.add(new CardIdentity("Tropical Island", ""));
cubeCards.add(new CardIdentity("Tundra", ""));
cubeCards.add(new CardIdentity("Turnabout", ""));
cubeCards.add(new CardIdentity("Ugin, the Spirit Dragon", ""));
cubeCards.add(new CardIdentity("Ulamog, the Ceaseless Hunger", ""));
cubeCards.add(new CardIdentity("Ulamog, the Infinite Gyre", ""));
cubeCards.add(new CardIdentity("Umezawa's Jitte", ""));
cubeCards.add(new CardIdentity("Unburial Rites", ""));
cubeCards.add(new CardIdentity("Underground Sea", ""));
cubeCards.add(new CardIdentity("Underworld Breach", ""));
cubeCards.add(new CardIdentity("Unholy Heat", ""));
cubeCards.add(new CardIdentity("Upheaval", ""));
cubeCards.add(new CardIdentity("Uro, Titan of Nature's Wrath", ""));
cubeCards.add(new CardIdentity("Urza's Saga", ""));
cubeCards.add(new CardIdentity("Urza, Lord High Artificer", ""));
cubeCards.add(new CardIdentity("Usher of the Fallen", ""));
cubeCards.add(new CardIdentity("Utopia Sprawl", ""));
cubeCards.add(new CardIdentity("Vampire Hexmage", ""));
cubeCards.add(new CardIdentity("Vampiric Tutor", ""));
cubeCards.add(new CardIdentity("Vendilion Clique", ""));
cubeCards.add(new CardIdentity("Venser, Shaper Savant", ""));
cubeCards.add(new CardIdentity("Verdant Catacombs", ""));
cubeCards.add(new CardIdentity("Vindicate", ""));
cubeCards.add(new CardIdentity("Volcanic Island", ""));
cubeCards.add(new CardIdentity("Volrath's Stronghold", ""));
cubeCards.add(new CardIdentity("Voltaic Visionary", ""));
cubeCards.add(new CardIdentity("Vraska, Golgari Queen", ""));
cubeCards.add(new CardIdentity("Vryn Wingmare", ""));
cubeCards.add(new CardIdentity("Walking Ballista", ""));
cubeCards.add(new CardIdentity("Wall of Omens", ""));
cubeCards.add(new CardIdentity("Wall of Roots", ""));
cubeCards.add(new CardIdentity("Wasteland", ""));
cubeCards.add(new CardIdentity("Waterlogged Grove", ""));
cubeCards.add(new CardIdentity("Watery Grave", ""));
cubeCards.add(new CardIdentity("Wear // Tear", ""));
cubeCards.add(new CardIdentity("Wheel of Fortune", ""));
cubeCards.add(new CardIdentity("Wheel of Misfortune", ""));
cubeCards.add(new CardIdentity("Whisperwood Elemental", ""));
cubeCards.add(new CardIdentity("Windswept Heath", ""));
cubeCards.add(new CardIdentity("Winter Orb", ""));
cubeCards.add(new CardIdentity("Wishclaw Talisman", ""));
cubeCards.add(new CardIdentity("Woe Strider", ""));
cubeCards.add(new CardIdentity("Wooded Foothills", ""));
cubeCards.add(new CardIdentity("Woodfall Primus", ""));
cubeCards.add(new CardIdentity("Worldly Tutor", ""));
cubeCards.add(new CardIdentity("Worn Powerstone", ""));
cubeCards.add(new CardIdentity("Wrath of God", ""));
cubeCards.add(new CardIdentity("Wrenn and Six", ""));
cubeCards.add(new CardIdentity("Wurmcoil Engine", ""));
cubeCards.add(new CardIdentity("Yawgmoth's Bargain", ""));
cubeCards.add(new CardIdentity("Yawgmoth's Will", ""));
cubeCards.add(new CardIdentity("Yorion, Sky Nomad", ""));
cubeCards.add(new CardIdentity("Young Pyromancer", ""));
cubeCards.add(new CardIdentity("Zealous Conscripts", ""));
}
}

View file

@ -153,6 +153,7 @@
<draftCube name="MTGO Vintage Cube July 2020" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeJuly2020"/>
<draftCube name="MTGO Vintage Cube December 2020" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeDecember2020"/>
<draftCube name="MTGO Vintage Cube July 2021" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeJuly2021"/>
<draftCube name="MTGO Vintage Cube February 2022" jar="mage-tournament-booster-draft.jar" className="mage.tournament.cubes.VintageCubeFebruary2022"/>
<draftCube name="SCG Con Cube 2018 December" jar="mage-tournament-booster-draft.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

@ -231,12 +231,12 @@
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client-java6</artifactId>
<version>1.31.0</version>
<version>1.33.0</version>
</dependency>
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client-jetty</artifactId>
<version>1.31.2</version>
<version>1.33.0</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>

View file

@ -1,2 +0,0 @@
#workaround to remove un-wanted ormlite logs, see https://github.com/magefree/mage/issues/8373
LogBackendType.*=ERROR

View file

@ -12,6 +12,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.target.common.TargetCreaturePermanent;
/**
@ -25,6 +26,7 @@ public final class AbandonReason extends CardImpl {
// Up to two target creatures each get +1/+0 and gain first strike until end of turn.
Effect effect = new BoostTargetEffect(1, 0, Duration.EndOfTurn);
effect.setOutcome(Outcome.Benefit);
effect.setText("Up to two target creatures each get +1/+0");
this.getSpellAbility().addEffect(effect);
effect = new GainAbilityTargetEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, "and gain first strike until end of turn");

View file

@ -2,22 +2,12 @@ package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect;
import mage.abilities.keyword.ProwessAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Library;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/**
*
@ -36,7 +26,7 @@ public final class AbbotOfKeralKeep extends CardImpl {
this.addAbility(new ProwessAbility());
// When Abbot of Keral Keep enters the battlefield, exile the top card of your library. Until end of turn, you may play that card.
this.addAbility(new EntersBattlefieldTriggeredAbility(new AbbotOfKeralKeepExileEffect()));
this.addAbility(new EntersBattlefieldTriggeredAbility(new ExileTopXMayPlayUntilEndOfTurnEffect(1)));
}
private AbbotOfKeralKeep(final AbbotOfKeralKeep card) {
@ -48,67 +38,3 @@ public final class AbbotOfKeralKeep extends CardImpl {
return new AbbotOfKeralKeep(this);
}
}
class AbbotOfKeralKeepExileEffect extends OneShotEffect {
public AbbotOfKeralKeepExileEffect() {
super(Outcome.Detriment);
this.staticText = "exile the top card of your library. Until end of turn, you may play that card";
}
public AbbotOfKeralKeepExileEffect(final AbbotOfKeralKeepExileEffect effect) {
super(effect);
}
@Override
public AbbotOfKeralKeepExileEffect copy() {
return new AbbotOfKeralKeepExileEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (sourcePermanent != null && controller != null && controller.getLibrary().hasCards()) {
Library library = controller.getLibrary();
Card card = library.getFromTop(game);
if (card != null) {
String exileName = sourcePermanent.getIdName() + " <this card may be played the turn it was exiled>";
controller.moveCardsToExile(card, source, game, true, source.getSourceId(), exileName);
ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Duration.EndOfTurn);
effect.setTargetPointer(new FixedTarget(card, game));
game.addEffect(effect, source);
}
return true;
}
return false;
}
}
class AbbotOfKeralKeepCastFromExileEffect extends AsThoughEffectImpl {
public AbbotOfKeralKeepCastFromExileEffect() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
staticText = "You may play the card from exile";
}
public AbbotOfKeralKeepCastFromExileEffect(final AbbotOfKeralKeepCastFromExileEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public AbbotOfKeralKeepCastFromExileEffect copy() {
return new AbbotOfKeralKeepCastFromExileEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
return source.isControlledBy(affectedControllerId)
&& objectId.equals(getTargetPointer().getFirst(game, source));
}
}

View file

@ -28,7 +28,6 @@ public final class AberrantResearcher extends CardImpl {
this.power = new MageInt(3);
this.toughness = new MageInt(2);
this.transformable = true;
this.secondSideCardClazz = mage.cards.p.PerfectedForm.class;
// Flying
@ -71,7 +70,7 @@ class AberrantResearcherEffect extends OneShotEffect {
.noneMatch(card -> card.isInstantOrSorcery(game))) {
return false;
}
new TransformSourceEffect(true).apply(game, source);
new TransformSourceEffect().apply(game, source);
return true;
}

View file

@ -1,26 +1,21 @@
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.Ability;
import mage.abilities.common.TransformIntoSourceTriggeredAbility;
import mage.abilities.effects.common.SacrificeEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.target.Target;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
*
* @author fireshoes
*/
public final class AbolisherOfBloodlines extends CardImpl {
@ -39,7 +34,11 @@ public final class AbolisherOfBloodlines extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
// When this creature transforms into Abolisher of Bloodlines, target opponent sacrifices three creatures.
this.addAbility(new AbolisherOfBloodlinesAbility());
Ability ability = new TransformIntoSourceTriggeredAbility(new SacrificeEffect(
StaticFilters.FILTER_PERMANENT_CREATURE, 3, "target opponent"
));
ability.addTarget(new TargetOpponent());
this.addAbility(ability);
}
private AbolisherOfBloodlines(final AbolisherOfBloodlines card) {
@ -51,44 +50,3 @@ public final class AbolisherOfBloodlines extends CardImpl {
return new AbolisherOfBloodlines(this);
}
}
class AbolisherOfBloodlinesAbility extends TriggeredAbilityImpl {
static final String RULE_TEXT = "When this creature transforms into Abolisher of Bloodlines, target opponent sacrifices three creatures";
public AbolisherOfBloodlinesAbility() {
super(Zone.BATTLEFIELD, new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, 3, "Target opponent"), false);
Target target = new TargetOpponent();
this.addTarget(target);
}
public AbolisherOfBloodlinesAbility(final AbolisherOfBloodlinesAbility ability) {
super(ability);
}
@Override
public AbolisherOfBloodlinesAbility copy() {
return new AbolisherOfBloodlinesAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TRANSFORMED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(sourceId)) {
Permanent permanent = game.getPermanent(sourceId);
if (permanent != null && permanent.isTransformed()) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return RULE_TEXT + '.';
}
}

View file

@ -21,7 +21,7 @@ public final class AbzanAdvantage extends CardImpl {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}");
// Target player sacrifices an enchantment. Bolster 1.
this.getSpellAbility().addEffect(new SacrificeEffect(StaticFilters.FILTER_ENCHANTMENT_PERMANENT, 1, "Target player"));
this.getSpellAbility().addEffect(new SacrificeEffect(StaticFilters.FILTER_PERMANENT_ENCHANTMENT, 1, "Target player"));
this.getSpellAbility().addEffect(new BolsterEffect(1));
this.getSpellAbility().addTarget(new TargetPlayer());
}

View file

@ -10,8 +10,7 @@ import mage.abilities.keyword.OutlastAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import java.util.UUID;
@ -20,14 +19,6 @@ import java.util.UUID;
*/
public final class AbzanBattlePriest extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent();
static {
filter.add(CardType.CREATURE.getPredicate());
filter.add(TargetController.YOU.getControllerPredicate());
filter.add(CounterType.P1P1.getPredicate());
}
public AbzanBattlePriest(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
this.subtype.add(SubType.HUMAN);
@ -44,7 +35,8 @@ public final class AbzanBattlePriest extends CardImpl {
Zone.BATTLEFIELD,
new GainAbilityAllEffect(
LifelinkAbility.getInstance(), Duration.WhileOnBattlefield,
filter, "Each creature you control with a +1/+1 counter on it has lifelink"
StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1,
"Each creature you control with a +1/+1 counter on it has lifelink"
)
));
}

View file

@ -11,21 +11,13 @@ import mage.abilities.keyword.OutlastAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
/**
*
* @author emerald000
*/
public final class AbzanFalconer extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent();
static {
filter.add(CardType.CREATURE.getPredicate());
filter.add(TargetController.YOU.getControllerPredicate());
filter.add(CounterType.P1P1.getPredicate());
}
public AbzanFalconer(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}");
@ -39,7 +31,11 @@ public final class AbzanFalconer extends CardImpl {
this.addAbility(new OutlastAbility(new ColoredManaCost(ColoredManaSymbol.W)));
// Each creature you control with a +1/+1 counter on it has flying.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, filter, "Each creature you control with a +1/+1 counter on it has flying")));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(
FlyingAbility.getInstance(),
Duration.WhileOnBattlefield,
StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1)
));
}
private AbzanFalconer(final AbzanFalconer card) {

View file

@ -0,0 +1,65 @@
package mage.cards.a;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.token.ThopterColorlessToken;
import mage.game.stack.StackObject;
import mage.target.TargetSpell;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AccessDenied extends CardImpl {
public AccessDenied(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}");
// Counter target spell. Create X 1/1 colorless Thopter artifact creature tokens with flying, where X is that spell's mana value.
this.getSpellAbility().addEffect(new AccessDeniedEffect());
this.getSpellAbility().addTarget(new TargetSpell());
}
private AccessDenied(final AccessDenied card) {
super(card);
}
@Override
public AccessDenied copy() {
return new AccessDenied(this);
}
}
class AccessDeniedEffect extends OneShotEffect {
AccessDeniedEffect() {
super(Outcome.Benefit);
staticText = "counter target spell. Create X 1/1 colorless Thopter " +
"artifact creature tokens with flying, where X is that spell's mana value";
}
private AccessDeniedEffect(final AccessDeniedEffect effect) {
super(effect);
}
@Override
public AccessDeniedEffect copy() {
return new AccessDeniedEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
StackObject stackObject = game.getStack().getStackObject(targetPointer.getFirst(game, source));
if (stackObject != null) {
game.getStack().counter(source.getFirstTarget(), source, game);
return new ThopterColorlessToken().putOntoBattlefield(stackObject.getManaValue(), game, source);
}
return false;
}
}

View file

@ -30,7 +30,6 @@ public final class AccursedWitch extends CardImpl {
this.power = new MageInt(4);
this.toughness = new MageInt(2);
this.transformable = true;
this.secondSideCardClazz = mage.cards.i.InfectiousCurse.class;
// Spells your opponents cast that target Accursed Witch cost {1} less to cast.

View file

@ -38,7 +38,7 @@ public final class AcidicSliver extends CardImpl {
ability.addTarget(new TargetAnyTarget());
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new GainAbilityAllEffect(ability,
Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURE_SLIVERS,
Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_ALL_SLIVERS,
"All Slivers have \"{2}, Sacrifice this permanent: This permanent deals 2 damage to any target.\"")));
}

View file

@ -30,7 +30,7 @@ public final class AcklayOfTheArena extends CardImpl {
this.addAbility(new MonstrosityAbility("{2}{R}{G}{W}", 1));
// Whenever a creature you control becomes monstrous, it fights target creature an opponent controls.
Ability ability = new BecomesMonstrousTriggeredAbility(new FightTargetsEffect("it fights target creature an opponent controls"));
Ability ability = new BecomesMonstrousTriggeredAbility(new FightTargetsEffect().setText("it fights target creature an opponent controls"));
ability.addTarget(new TargetOpponentsCreaturePermanent());
this.addAbility(ability);

View file

@ -0,0 +1,85 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.keyword.ReconfigureAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AcquisitionOctopus extends CardImpl {
public AcquisitionOctopus(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{U}");
this.subtype.add(SubType.EQUIPMENT);
this.subtype.add(SubType.OCTOPUS);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// When Acquisition Octopus or equipped creature deals combat damage to a player, draw a card.
this.addAbility(new AcquisitionOctopusTriggeredAbility());
// Reconfigure {2}
this.addAbility(new ReconfigureAbility("{2}"));
}
private AcquisitionOctopus(final AcquisitionOctopus card) {
super(card);
}
@Override
public AcquisitionOctopus copy() {
return new AcquisitionOctopus(this);
}
}
class AcquisitionOctopusTriggeredAbility extends TriggeredAbilityImpl {
AcquisitionOctopusTriggeredAbility() {
super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1));
}
private AcquisitionOctopusTriggeredAbility(final AcquisitionOctopusTriggeredAbility ability) {
super(ability);
}
@Override
public AcquisitionOctopusTriggeredAbility copy() {
return new AcquisitionOctopusTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!((DamagedEvent) event).isCombatDamage()) {
return false;
}
if (getSourceId().equals(event.getSourceId())) {
return true;
}
Permanent permanent = getSourcePermanentOrLKI(game);
return permanent != null && event.getSourceId().equals(permanent.getAttachedTo());
}
@Override
public String getRule() {
return "Whenever {this} or equipped creature deals combat damage to a player, draw a card.";
}
}

View file

@ -30,7 +30,7 @@ public final class AcquisitionsExpert extends CardImpl {
// When Acquisitions Expert enters the battlefield, target opponent reveals a number of cards from their hand equal to the number of creatures in your party. You choose one of those cards. That player discards that card.
Ability ability = new EntersBattlefieldTriggeredAbility(
new DiscardCardYouChooseTargetEffect(TargetController.ANY, PartyCount.instance)
new DiscardCardYouChooseTargetEffect(TargetController.OPPONENT, PartyCount.instance)
.setText("target opponent reveals a number of cards from their hand " +
"equal to the number of creatures in your party. You choose one of those cards. " +
"That player discards that card. " + PartyCount.getReminder())

View file

@ -22,10 +22,10 @@ public final class AcrobaticManeuver extends CardImpl {
// Exile target creature you control, then return that card to the battlefield under its owner's control.
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
this.getSpellAbility().addEffect(new ExileTargetForSourceEffect());
this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false));
this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false).concatBy(","));
// Draw a card.
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1));
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("<br>"));
}
private AcrobaticManeuver(final AcrobaticManeuver card) {

View file

@ -10,8 +10,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.StaticFilters;
import mage.target.common.TargetCreaturePermanent;
/**
@ -20,15 +19,9 @@ import mage.target.common.TargetCreaturePermanent;
*/
public final class ActOfAggression extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls");
static {
filter.add(TargetController.OPPONENT.getControllerPredicate());
}
public ActOfAggression(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{R/P}{R/P}");
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE));
this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn));
this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap that creature"));
this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn).setText("It gains haste until end of turn."));

View file

@ -120,7 +120,8 @@ class AdarkarValkyrieDelayedTriggeredAbility extends DelayedTriggeredAbility {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (((ZoneChangeEvent) event).isDiesEvent()
&& mor.refersTo(((ZoneChangeEvent) event).getTarget(), game)) {
&& mor.refersTo(((ZoneChangeEvent) event).getTarget(), game)
&& game.getState().getZone(event.getTargetId()) == Zone.GRAVEYARD) { // must be in the graveyard
getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game));
return true;

View file

@ -1,11 +1,8 @@
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.common.SourceOnBattlefieldControlUnchangedCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.IndestructibleAbility;
@ -17,7 +14,8 @@ import mage.constants.SubType;
import mage.filter.FilterPermanent;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.target.TargetPermanent;
import mage.watchers.common.LostControlWatcher;
import java.util.UUID;
/**
* @author Loki
@ -41,13 +39,10 @@ public final class AegisAngel extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
// When Aegis Angel enters the battlefield, another target permanent is indestructible for as long as you control Aegis Angel.
ConditionalContinuousEffect effect = new ConditionalContinuousEffect(
new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.Custom),
new SourceOnBattlefieldControlUnchangedCondition(),
"another target permanent is indestructible for as long as you control Aegis Angel");
Ability ability = new EntersBattlefieldTriggeredAbility(effect, false);
Ability ability = new EntersBattlefieldTriggeredAbility(new GainAbilityTargetEffect(
IndestructibleAbility.getInstance(), Duration.WhileControlled
), false);
ability.addTarget(new TargetPermanent(filter));
ability.addWatcher(new LostControlWatcher());
this.addAbility(ability);
}
@ -59,5 +54,4 @@ public final class AegisAngel extends CardImpl {
public AegisAngel copy() {
return new AegisAngel(this);
}
}

View file

@ -2,26 +2,14 @@ package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect;
import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Library;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/**
*
@ -41,7 +29,10 @@ public final class AerialCaravan extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
// {1}{U}{U}: Exile the top card of your library. Until end of turn, you may play that card.
this.addAbility(new SimpleActivatedAbility(new AerialCaravanExileEffect(), new ManaCostsImpl("{1}{U}{U}")));
this.addAbility(new SimpleActivatedAbility(
new ExileTopXMayPlayUntilEndOfTurnEffect(1).setText("Exile the top card of your library. " +
"Until end of turn, you may play that card. <i>(Reveal the card as you exile it.)</i>"),
new ManaCostsImpl("{1}{U}{U}")));
}
private AerialCaravan(final AerialCaravan card) {
@ -53,39 +44,3 @@ public final class AerialCaravan extends CardImpl {
return new AerialCaravan(this);
}
}
class AerialCaravanExileEffect extends OneShotEffect {
public AerialCaravanExileEffect() {
super(Outcome.Detriment);
this.staticText = "Exile the top card of your library. Until end of turn, you may play that card";
}
public AerialCaravanExileEffect(final AerialCaravanExileEffect effect) {
super(effect);
}
@Override
public AerialCaravanExileEffect copy() {
return new AerialCaravanExileEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (sourcePermanent != null && controller != null && controller.getLibrary().hasCards()) {
Library library = controller.getLibrary();
Card card = library.getFromTop(game);
if (card != null) {
String exileName = sourcePermanent.getIdName() + " <this card may be played the turn it was exiled>";
controller.moveCardsToExile(card, source, game, true, source.getSourceId(), exileName);
ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn);
effect.setTargetPointer(new FixedTarget(card, game));
game.addEffect(effect, source);
}
return true;
}
return false;
}
}

View file

@ -24,7 +24,7 @@ import mage.target.TargetPermanent;
*/
public final class AerialModification extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("creature or vehicle");
private static final FilterPermanent filter = new FilterPermanent("creature or Vehicle");
static {
filter.add(Predicates.or(CardType.CREATURE.getPredicate(),

View file

@ -0,0 +1,115 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.common.LandsYouControlHint;
import mage.abilities.keyword.CrewAbility;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.FilterCard;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.common.FilterBasicLandCard;
import mage.filter.common.FilterLandPermanent;
import mage.filter.predicate.permanent.DefendingPlayerControlsPredicate;
import mage.game.Controllable;
import mage.game.Game;
import mage.target.common.TargetCardInLibrary;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author TheElk801
*/
public final class AerialSurveyor extends CardImpl {
private static final FilterCard filter = new FilterBasicLandCard(SubType.PLAINS);
public AerialSurveyor(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{W}");
this.subtype.add(SubType.VEHICLE);
this.power = new MageInt(3);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever Aerial Surveyor attacks, if defending player controls more lands than you, search your library for a basic Plains card, put it onto the battlefield tapped, then shuffle.
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
new AttacksTriggeredAbility(
new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), true)
), AerialSurveyorCondition.instance, "Whenever {this} attacks, if defending player " +
"controls more lands than you, search your library for a basic Plains card, " +
"put it onto the battlefield tapped, then shuffle."
).addHint(LandsYouControlHint.instance).addHint(AerialSurveyorHint.instance));
// Crew 2
this.addAbility(new CrewAbility(2));
}
private AerialSurveyor(final AerialSurveyor card) {
super(card);
}
@Override
public AerialSurveyor copy() {
return new AerialSurveyor(this);
}
}
enum AerialSurveyorCondition implements Condition {
instance;
private static final FilterPermanent filter = new FilterLandPermanent();
static {
filter.add(DefendingPlayerControlsPredicate.instance);
}
@Override
public boolean apply(Game game, Ability source) {
return game.getBattlefield().count(
filter, source.getControllerId(), source.getSourceId(), game
) > game.getBattlefield().count(
StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND,
source.getControllerId(), source.getSourceId(), game
);
}
}
enum AerialSurveyorHint implements Hint {
instance;
@Override
public String getText(Game game, Ability ability) {
return game.getBattlefield()
.getActivePermanents(
StaticFilters.FILTER_LAND,
ability.getControllerId(),
ability.getSourceId(), game
).stream()
.map(Controllable::getControllerId)
.filter(game.getOpponents(ability.getControllerId())::contains)
.collect(Collectors.toMap(Function.identity(), u -> 1, Integer::sum))
.entrySet()
.stream()
.filter(entry -> game.getPlayer(entry.getKey()) != null)
.map(entry -> "Lands " + game.getPlayer(entry.getKey()).getName() + " controls: " + entry.getValue())
.collect(Collectors.joining("<br>"));
}
@Override
public AerialSurveyorHint copy() {
return this;
}
}

View file

@ -1,8 +1,6 @@
package mage.cards.a;
import java.util.UUID;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DamageMultiEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
@ -28,11 +26,8 @@ public final class AerialVolley extends CardImpl {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{G}");
// Aerial Volley deals 3 damage divided as you choose among one, two, or three target creatures with flying.
Effect effect = new DamageMultiEffect(3);
effect.setText("{this} deals 3 damage divided as you choose among one, two, or three target creatures with flying");
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addEffect(new DamageMultiEffect(3));
this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3, filter));
}
private AerialVolley(final AerialVolley card) {

View file

@ -36,8 +36,7 @@ public final class AetherChaser extends CardImpl {
this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2)));
// Whenever Aether Chaser attacks, you may pay {E}{E}. If you do, create a 1/1 colorless Servo artifact creature token.
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new CreateTokenEffect(new ServoToken()), new PayEnergyCost(2)), false,
"Whenever {this} attacks you may pay {E}{E}. If you do, create a 1/1 colorless Servo artifact creature token."));
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new CreateTokenEffect(new ServoToken()), new PayEnergyCost(2))));
}
private AetherChaser(final AetherChaser card) {

View file

@ -34,8 +34,7 @@ public final class AetherHerder extends CardImpl {
this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2)));
// Whenever Aether Herder attacks, you may pay {E}{E}. If you do, create a 1/1 colorless Servo artifact creature token.
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new CreateTokenEffect(new ServoToken()), new PayEnergyCost(2)), false,
"Whenever {this} attacks you may pay {E}{E}. If you do, create a 1/1 colorless Servo artifact creature token."));
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new CreateTokenEffect(new ServoToken()), new PayEnergyCost(2))));
}
private AetherHerder(final AetherHerder card) {

View file

@ -36,8 +36,7 @@ public final class AetherInspector extends CardImpl {
this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2)));
// Whenever Aether Inspector attacks, you may pay {E}{E}. If you do, create a 1/1 colorless Servo artifact creature token.
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new CreateTokenEffect(new ServoToken()), new PayEnergyCost(2)), false,
"Whenever {this} attacks you may pay {E}{E}. If you do, create a 1/1 colorless Servo artifact creature token."));
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new CreateTokenEffect(new ServoToken()), new PayEnergyCost(2))));
}
private AetherInspector(final AetherInspector card) {

View file

@ -67,7 +67,7 @@ class AetherRiftEffect extends OneShotEffect {
Card card = controller.discardOne(true, false, source, game);
if (card != null && card.isCreature(game)) {
Effect returnEffect = new ReturnFromGraveyardToBattlefieldTargetEffect();
returnEffect.setTargetPointer(new FixedTarget(card.getId()));
returnEffect.setTargetPointer(new FixedTarget(card.getId(), game));
Effect doEffect = new DoUnlessAnyPlayerPaysEffect(returnEffect, new PayLifeCost(5),
"Pay 5 life to prevent " + card.getLogName() + " to return from graveyard to battlefield?");
return doEffect.apply(game, source);

View file

@ -37,8 +37,7 @@ public final class AetherSwooper extends CardImpl {
this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2)));
// Whenever Aether Swooper attacks, you may pay {E}{E}. If you do, create a 1/1 colorless Servo artifact creature token.
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new CreateTokenEffect(new ServoToken()), new PayEnergyCost(2)), false,
"Whenever {this} attacks you may pay {E}{E}. If you do, create a 1/1 colorless Servo artifact creature token."));
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new CreateTokenEffect(new ServoToken()), new PayEnergyCost(2))));
}
private AetherSwooper(final AetherSwooper card) {

View file

@ -1,7 +1,5 @@
package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
@ -10,31 +8,30 @@ import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.game.Game;
import mage.target.common.TargetAnyTarget;
import mage.watchers.common.CastSpellLastTurnWatcher;
import java.util.UUID;
/**
*
* @author emerald000
*/
public final class AetherfluxReservoir extends CardImpl {
public AetherfluxReservoir(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
// Whenever you cast a spell, you gain 1 life for each spell you've cast this turn.
Ability abilityGainLife = new SpellCastControllerTriggeredAbility(new GainLifeEffect(new AetherfluxReservoirDynamicValue()), false);
abilityGainLife.addHint(new ValueHint("You've cast spells this turn", new AetherfluxReservoirDynamicValue()));
this.addAbility(abilityGainLife);
this.addAbility(new SpellCastControllerTriggeredAbility(new GainLifeEffect(
AetherfluxReservoirDynamicValue.instance, "you gain 1 life for each spell you've cast this turn"
), false));
// Pay 50 life: Aetherflux Reservoir deals 50 damage to any target.
Ability abilityPayLife = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(50), new PayLifeCost(50));
Ability abilityPayLife = new SimpleActivatedAbility(new DamageTargetEffect(50), new PayLifeCost(50));
abilityPayLife.addTarget(new TargetAnyTarget());
this.addAbility(abilityPayLife);
}
@ -49,20 +46,20 @@ public final class AetherfluxReservoir extends CardImpl {
}
}
class AetherfluxReservoirDynamicValue implements DynamicValue {
enum AetherfluxReservoirDynamicValue implements DynamicValue {
instance;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class);
if(watcher != null) {
return watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(sourceAbility.getControllerId());
}
return 0;
return game
.getState()
.getWatcher(CastSpellLastTurnWatcher.class)
.getAmountOfSpellsPlayerCastOnCurrentTurn(sourceAbility.getControllerId());
}
@Override
public AetherfluxReservoirDynamicValue copy() {
return new AetherfluxReservoirDynamicValue();
return this;
}
@Override
@ -74,5 +71,4 @@ class AetherfluxReservoirDynamicValue implements DynamicValue {
public String getMessage() {
return "spell you've cast this turn";
}
}

View file

@ -1,7 +1,5 @@
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@ -16,8 +14,9 @@ import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import java.util.UUID;
/**
*
* @author fireshoes
*/
public final class AetherstreamLeopard extends CardImpl {
@ -36,8 +35,9 @@ public final class AetherstreamLeopard extends CardImpl {
this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(1)));
// Whenever Aetherstream Leopard attacks, you may pay {E}. If you do, it gets +2/+0 until end of turn.
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new BoostSourceEffect(2, 0, Duration.EndOfTurn), new PayEnergyCost(1)), false,
"Whenever {this} attacks you may pay {E}. If you do, it gets +2/+0 until end of turn."));
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new BoostSourceEffect(
2, 0, Duration.EndOfTurn
).setText("it gets +2/+0 until end of turn"), new PayEnergyCost(1))));
}
private AetherstreamLeopard(final AetherstreamLeopard card) {

View file

@ -31,7 +31,7 @@ public final class Aethertow extends CardImpl {
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
// Conspire
this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE));
this.addAbility(new ConspireAbility(ConspireAbility.ConspireTargets.ONE));
}
private Aethertow(final Aethertow card) {

View file

@ -35,7 +35,7 @@ public final class AetherwindBasker extends CardImpl {
this.addAbility(TrampleAbility.getInstance());
// Whenever Aetherwind Basker enters the battlefield or attacks, you get {E} for each creature you control.
this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new GetEnergyCountersControllerEffect(new PermanentsOnBattlefieldCount(StaticFilters.FILTER_PERMANENT_CREATURE_CONTROLLED, null))));
this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new GetEnergyCountersControllerEffect(new PermanentsOnBattlefieldCount(StaticFilters.FILTER_PERMANENT_CREATURE_CONTROLLED))));
// Pay {E}: Aetherwind Basker gets +1/+1 until end of turn.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 1, Duration.EndOfTurn), new PayEnergyCost(1)));

View file

@ -28,8 +28,8 @@ public final class AffectionateIndrik extends CardImpl {
// When Affectionate Indrik enters the battlefield, you may have it fight target creature you don't control.
Ability ability = new EntersBattlefieldTriggeredAbility(
new FightTargetSourceEffect()
.setText("you may have it fight "
+ "target creature you don't control"),
.setText("you may have it fight target creature you don't control. " +
"<i>(Each deals damage equal to its power to the other.)</i>"),
true
);
ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL));

View file

@ -20,7 +20,6 @@ public final class AfflictedDeserter extends CardImpl {
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.WEREWOLF);
this.transformable = true;
this.secondSideCardClazz = mage.cards.w.WerewolfRansacker.class;
this.power = new MageInt(3);

View file

@ -91,7 +91,7 @@ class AgitatorAntEffect extends OneShotEffect {
if (permanent == null || !permanent.addCounters(CounterType.P1P1.createInstance(2), player.getId(), source, game)) {
continue;
}
new GoadTargetEffect().setTargetPointer(new FixedTarget(permanent, game)).apply(game, source);
game.addEffect(new GoadTargetEffect().setTargetPointer(new FixedTarget(permanent, game)), source);
}
return true;
}

View file

@ -1,6 +1,5 @@
package mage.cards.a;
import mage.ObjectColor;
import mage.abilities.condition.common.KickedCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.dynamicvalue.common.TargetPermanentPowerCount;
@ -10,9 +9,7 @@ import mage.abilities.keyword.KickerAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.filter.StaticFilters;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
@ -22,12 +19,6 @@ import java.util.UUID;
*/
public final class AgonizingDemise extends CardImpl {
private static final FilterCreaturePermanent filterNonBlackCreature = new FilterCreaturePermanent("nonblack creature");
static {
filterNonBlackCreature.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK)));
}
public AgonizingDemise(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}");
@ -36,7 +27,7 @@ public final class AgonizingDemise extends CardImpl {
// Destroy target nonblack creature. It can't be regenerated.
this.getSpellAbility().addEffect(new DestroyTargetEffect(true));
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filterNonBlackCreature));
this.getSpellAbility().addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK));
// If Agonizing Demise was kicked, it deals damage equal to that creature's power to the creature's controller.
this.getSpellAbility().addEffect(new ConditionalOneShotEffect(

View file

@ -33,7 +33,7 @@ public final class Agoraphobia extends CardImpl {
// Enchant creature
TargetPermanent auraTarget = new TargetCreaturePermanent();
this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility));
this.getSpellAbility().addEffect(new AttachEffect(Outcome.UnboostCreature));
Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability);

View file

@ -1,9 +1,9 @@
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.effects.common.continuous.BoostAllEffect;
import mage.cards.CardImpl;
@ -27,7 +27,6 @@ public final class AgrusKosWojekVeteran extends CardImpl {
static {
filterRed.add(new ColorPredicate(ObjectColor.RED));
filterWhite.add(new ColorPredicate(ObjectColor.WHITE));
}
public AgrusKosWojekVeteran(UUID ownerId, CardSetInfo setInfo) {
@ -40,8 +39,9 @@ public final class AgrusKosWojekVeteran extends CardImpl {
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Whenever Agrus Kos, Wojek Veteran attacks, attacking red creatures get +2/+0 and attacking white creatures get +0/+2 until end of turn.
this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect(2, 0, Duration.EndOfTurn, filterRed, false), false));
this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect(0, 2, Duration.EndOfTurn, filterWhite, false), false));
Ability ability = new AttacksTriggeredAbility(new BoostAllEffect(2, 0, Duration.EndOfTurn, filterRed, false), false);
ability.addEffect(new BoostAllEffect(0, 2, Duration.EndOfTurn, filterWhite, false).concatBy("and"));
this.addAbility(ability);
}
private AgrusKosWojekVeteran(final AgrusKosWojekVeteran card) {

View file

@ -10,7 +10,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.StaticFilters;
/**
*
@ -26,7 +26,7 @@ public final class AhnCropChampion extends CardImpl {
this.toughness = new MageInt(4);
// You may exert Ahn-Crop Champion as it attacks. When you do, untap all other creatures you control.
addAbility(new ExertAbility(new BecomesExertSourceTriggeredAbility(new UntapAllControllerEffect(new FilterControlledCreaturePermanent("creatures you control"), null, false))));
addAbility(new ExertAbility(new BecomesExertSourceTriggeredAbility(new UntapAllControllerEffect(StaticFilters.FILTER_CONTROLLED_CREATURES, null, false))));
}
private AhnCropChampion(final AhnCropChampion card) {

View file

@ -25,7 +25,7 @@ import mage.watchers.common.RevoltWatcher;
public final class AidFromTheCowl extends CardImpl {
private static final String ruleText = "<i>Revolt</i> &mdash; At the beginning of your end step, if a permanent you controlled left the battlefield this turn, "
+ "reveal the top card of your library. If it's a permanent card, you may put it onto the battlefield. Otherwise, put it on the bottom of your library.";
+ "reveal the top card of your library. If it's a permanent card, you may put it onto the battlefield. Otherwise, you may put it on the bottom of your library.";
public AidFromTheCowl(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}{G}");

View file

@ -0,0 +1,50 @@
package mage.cards.a;
import java.util.UUID;
import mage.abilities.Mode;
import mage.abilities.effects.common.ExileFromZoneTargetEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.target.TargetPermanent;
import mage.target.common.TargetOpponent;
/**
*
* @author weirddan455
*/
public final class AimForTheHead extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent(SubType.ZOMBIE, "Zombie");
public AimForTheHead(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}");
// Choose one
// Exile target Zombie.
this.getSpellAbility().addEffect(new ExileTargetEffect());
this.getSpellAbility().addTarget(new TargetPermanent(filter));
// Target opponent exiles two cards from their hand.
Mode mode = new Mode(new ExileFromZoneTargetEffect(
Zone.HAND, StaticFilters.FILTER_CARD_CARDS, 2, false
));
mode.addTarget(new TargetOpponent());
this.getSpellAbility().addMode(mode);
}
private AimForTheHead(final AimForTheHead card) {
super(card);
}
@Override
public AimForTheHead copy() {
return new AimForTheHead(this);
}
}

View file

@ -11,8 +11,7 @@ import mage.abilities.keyword.OutlastAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
/**
*
@ -20,16 +19,6 @@ import mage.filter.FilterPermanent;
*/
public final class AinokBondKin extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent();
static {
filter.add(CardType.CREATURE.getPredicate());
filter.add(TargetController.YOU.getControllerPredicate());
filter.add(CounterType.P1P1.getPredicate());
}
static final String rule = "Each creature you control with a +1/+1 counter on it has first strike";
public AinokBondKin(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}");
this.subtype.add(SubType.DOG);
@ -42,8 +31,10 @@ public final class AinokBondKin extends CardImpl {
this.addAbility(new OutlastAbility(new ManaCostsImpl("{1}{W}")));
// Each creature you control with a +1/+1 counter on it has first strike.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter, rule)));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(
FirstStrikeAbility.getInstance(),
Duration.WhileOnBattlefield,
StaticFilters.FILTER_EACH_CONTROLLED_CREATURE_P1P1)));
}
private AinokBondKin(final AinokBondKin 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.common.EntersBattlefieldTriggeredAbility;
@ -16,8 +14,9 @@ import mage.constants.CardType;
import mage.constants.SubType;
import mage.watchers.common.RevoltWatcher;
import java.util.UUID;
/**
*
* @author Styxo
*/
public final class AirdropAeronauts extends CardImpl {
@ -34,10 +33,10 @@ public final class AirdropAeronauts extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
// <i>Revolt</i> &mdash; When Airdrop Aeronauts enters the battlefield, if a permanent you controlled left the battlefield this turn, you gain 5 life.
Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(
new GainLifeEffect(5), false), RevoltCondition.instance,
"<i>Revolt</i> &mdash; When {this} enters the battlefield, if a permanent you controlled left"
+ " the battlefield this turn, you gain 5 life."
Ability ability = new ConditionalInterveningIfTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new GainLifeEffect(5), false),
RevoltCondition.instance, "When {this} enters the battlefield, " +
"if a permanent you controlled left the battlefield this turn, you gain 5 life."
);
ability.setAbilityWord(AbilityWord.REVOLT);
this.addAbility(ability, new RevoltWatcher());

View file

@ -1,27 +1,27 @@
package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.effects.common.GetEmblemEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.game.command.emblems.AjaniAdversaryOfTyrantsEmblem;
import mage.target.TargetPermanent;
import mage.target.common.TargetCardInYourGraveyard;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author TheElk801
*/
public final class AjaniAdversaryOfTyrants extends CardImpl {
@ -38,11 +38,11 @@ public final class AjaniAdversaryOfTyrants extends CardImpl {
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
this.setStartingLoyalty(4);
// +1: Put a +1/+1 counter on each of up to two target creatures.
Ability ability = new LoyaltyAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 1);
ability.addTarget(new TargetCreaturePermanent(0, 2));
ability.addTarget(new TargetPermanent(0, 2, StaticFilters.FILTER_PERMANENT_CREATURES));
this.addAbility(ability);
// 2: Return target creature card with converted mana cost 2 or less from your graveyard to the battlefield.

View file

@ -4,7 +4,6 @@ package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.dynamicvalue.common.ControllerLifeCount;
import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects;
@ -34,7 +33,7 @@ public final class AjaniCallerOfThePride extends CardImpl {
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
this.setStartingLoyalty(4);
// +1: Put a +1/+1 counter on up to one target creature.
Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance());
effect.setText("Put a +1/+1 counter on up to one target creature");

View file

@ -4,7 +4,6 @@ package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.Effects;
@ -35,7 +34,7 @@ public final class AjaniGoldmane extends CardImpl {
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
this.setStartingLoyalty(4);
// +1: You gain 2 life.
this.addAbility(new LoyaltyAbility(new GainLifeEffect(2), 1));

View file

@ -2,7 +2,6 @@ package mage.cards.a;
import mage.abilities.Ability;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
@ -31,7 +30,7 @@ public final class AjaniInspiringLeader extends CardImpl {
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5));
this.setStartingLoyalty(5);
// +2: You gain 2 life. Put two +1/+1 counters on up to one target creature.
Ability ability = new LoyaltyAbility(new GainLifeEffect(2), 2);

View file

@ -4,7 +4,6 @@ package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LookLibraryAndPickControllerEffect;
import mage.abilities.effects.common.counter.DistributeCountersEffect;
@ -43,7 +42,7 @@ public final class AjaniMentorOfHeroes extends CardImpl {
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
this.setStartingLoyalty(4);
// +1: Distribute three +1/+1 counters among one, two, or three target creatures you control
Ability ability = new LoyaltyAbility(new DistributeCountersEffect(CounterType.P1P1, 3, false, "one, two, or three target creatures you control"), 1);

View file

@ -3,7 +3,6 @@ package mage.cards.a;
import java.util.UUID;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.GetEmblemEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
@ -44,7 +43,7 @@ public final class AjaniSteadfast extends CardImpl {
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
this.setStartingLoyalty(4);
// +1: Until end of turn, up to one target creature gets +1/+1 and gains first strike, vigilance, and lifelink.
Effect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn);

View file

@ -2,7 +2,6 @@ package mage.cards.a;
import mage.abilities.Ability;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
@ -32,7 +31,7 @@ public final class AjaniStrengthOfThePride extends CardImpl {
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5));
this.setStartingLoyalty(5);
// +1: You gain life equal to the number of creatures you control plus the number of planeswalkers you control.
this.addAbility(new LoyaltyAbility(new GainLifeEffect(

View file

@ -2,7 +2,6 @@ package mage.cards.a;
import mage.abilities.Ability;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
@ -38,7 +37,7 @@ public final class AjaniTheGreathearted extends CardImpl {
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5));
this.setStartingLoyalty(5);
// Creatures you control have vigilance.
this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(
@ -52,7 +51,7 @@ public final class AjaniTheGreathearted extends CardImpl {
// -2: Put a +1/+1 counter on each creature you control and a loyalty counter on each other planeswalker you control.
Ability ability = new LoyaltyAbility(new AddCountersAllEffect(
CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURES
CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE
), -2);
ability.addEffect(new AddCountersAllEffect(
CounterType.LOYALTY.createInstance(), filter

View file

@ -3,7 +3,6 @@ package mage.cards.a;
import java.util.UUID;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.effects.common.ExileAndGainLifeEqualPowerTargetEffect;
import mage.abilities.effects.common.RevealLibraryPutIntoHandEffect;
import mage.abilities.effects.common.counter.AddCountersAllEffect;
@ -41,7 +40,7 @@ public final class AjaniUnyielding extends CardImpl {
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
this.setStartingLoyalty(4);
// +2: Reveal the top three cards of your library. Put all nonland permanent cards revealed this way into your hand and the rest on the bottom of your library in any order.
this.addAbility(new LoyaltyAbility(new RevealLibraryPutIntoHandEffect(3, nonlandPermanentFilter, Zone.LIBRARY), 2));
@ -53,7 +52,7 @@ public final class AjaniUnyielding extends CardImpl {
// -9: Put five +1/+1 counters on each creature you control and five loyalty counters on each other planeswalker you control.
LoyaltyAbility ajaniAbility3 = new LoyaltyAbility(new AddCountersAllEffect(CounterType.P1P1.createInstance(5), new FilterControlledCreaturePermanent()), -9);
ajaniAbility3.addEffect(new AddCountersAllEffect(CounterType.LOYALTY.createInstance(5), planeswalkerFilter));
ajaniAbility3.addEffect(new AddCountersAllEffect(CounterType.LOYALTY.createInstance(5), planeswalkerFilter).setText("and five loyalty counters on each other planeswalker you control"));
this.addAbility(ajaniAbility3);
}

View file

@ -3,7 +3,6 @@ package mage.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.dynamicvalue.common.ControllerLifeCount;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.RevealCardsFromLibraryUntilEffect;
@ -32,7 +31,7 @@ public final class AjaniValiantProtector extends CardImpl {
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
this.setStartingLoyalty(4);
// +2: Put two +1/+1 counters on up to one target creature.
Ability ability = new LoyaltyAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)), 2);

View file

@ -3,7 +3,6 @@ package mage.cards.a;
import java.util.UUID;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.effects.Effects;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.DestroyAllControlledTargetEffect;
@ -36,7 +35,7 @@ public final class AjaniVengeant extends CardImpl {
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(3));
this.setStartingLoyalty(3);
// +1: Target permanent doesn't untap during its controller's next untap step.
LoyaltyAbility ability1 = new LoyaltyAbility(new DontUntapInControllersNextUntapStepTargetEffect(), 1);

View file

@ -2,7 +2,6 @@ package mage.cards.a;
import mage.abilities.Ability;
import mage.abilities.LoyaltyAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.dynamicvalue.common.ControllerLifeCount;
import mage.abilities.dynamicvalue.common.CreaturesYouControlCount;
import mage.abilities.effects.common.GainLifeEffect;
@ -29,7 +28,7 @@ public final class AjaniWiseCounselor extends CardImpl {
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.AJANI);
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5));
this.setStartingLoyalty(5);
// +2: You gain 1 life for each creature you control.
this.addAbility(new LoyaltyAbility(new GainLifeEffect(CreaturesYouControlCount.instance)

View file

@ -37,7 +37,7 @@ public final class AjanisInfluence extends CardImpl {
this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect(
StaticValue.get(5), false, StaticValue.get(1), filter,
Zone.LIBRARY, false, true, false, Zone.HAND, true, false, false
).setBackInRandomOrder(true).setText("Look at the top five cards of your library. "
).setBackInRandomOrder(true).setText("<br>Look at the top five cards of your library. "
+ "You may reveal a white card from among them and put it into your hand. "
+ "Put the rest on the bottom of your library in a random order.")
);

View file

@ -0,0 +1,53 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility;
import mage.abilities.effects.common.AdditionalCombatPhaseEffect;
import mage.abilities.effects.common.UntapAllEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.permanent.ModifiedPredicate;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AkkiBattleSquad extends CardImpl {
private static final FilterControlledCreaturePermanent filter
= new FilterControlledCreaturePermanent("modified creatures you control");
static {
filter.add(ModifiedPredicate.instance);
}
public AkkiBattleSquad(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}");
this.subtype.add(SubType.GOBLIN);
this.subtype.add(SubType.SAMURAI);
this.power = new MageInt(6);
this.toughness = new MageInt(6);
// Whenever one or more modified creatures you control attack, untap all modified creatures you control. After this combat phase, there is an additional combat phase. This ability triggers only once each turn.
Ability ability = new AttacksCreatureYouControlTriggeredAbility(
new UntapAllEffect(filter), false, filter
).setTriggerPhrase("Whenever one or more modified creatures you control attack, ").setTriggersOnce(true);
ability.addEffect(new AdditionalCombatPhaseEffect());
this.addAbility(ability);
}
private AkkiBattleSquad(final AkkiBattleSquad card) {
super(card);
}
@Override
public AkkiBattleSquad copy() {
return new AkkiBattleSquad(this);
}
}

View file

@ -1,4 +1,3 @@
package mage.cards.a;
import java.util.UUID;
@ -13,7 +12,7 @@ import mage.abilities.keyword.FirstStrikeAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterAttackingCreature;
import mage.filter.StaticFilters;
/**
*
@ -29,9 +28,8 @@ public final class AkkiCoalflinger extends CardImpl {
this.power = new MageInt(2);
this.toughness = new MageInt(2);
this.addAbility(FirstStrikeAbility.getInstance());
Effect effect = new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, new FilterAttackingCreature());
effect.setText("Attacking creatures gain first strike until end of turn");
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ColoredManaCost(ColoredManaSymbol.R));
Effect effect = new GainAbilityAllEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES);
Ability ability = new SimpleActivatedAbility(effect, new ColoredManaCost(ColoredManaSymbol.R));
ability.addCost(new TapSourceCost());
this.addAbility(ability);
}

View file

@ -0,0 +1,53 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.common.DiesCreatureTriggeredAbility;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.permanent.ModifiedPredicate;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.game.permanent.token.SpiritToken;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AkkiEmberKeeper extends CardImpl {
private static final FilterPermanent filter
= new FilterControlledCreaturePermanent("a nontoken modified creature you control");
static {
filter.add(TokenPredicate.FALSE);
filter.add(ModifiedPredicate.instance);
}
public AkkiEmberKeeper(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{R}");
this.subtype.add(SubType.GOBLIN);
this.subtype.add(SubType.WARRIOR);
this.power = new MageInt(2);
this.toughness = new MageInt(1);
// Whenever a nontoken modified creature you control dies, create a 1/1 colorless Spirit creature token.
this.addAbility(new DiesCreatureTriggeredAbility(
new CreateTokenEffect(new SpiritToken()), false, filter
));
}
private AkkiEmberKeeper(final AkkiEmberKeeper card) {
super(card);
}
@Override
public AkkiEmberKeeper copy() {
return new AkkiEmberKeeper(this);
}
}

View file

@ -0,0 +1,43 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.common.AttacksAloneControlledTriggeredAbility;
import mage.abilities.costs.common.DiscardCardCost;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.StaticFilters;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AkkiRonin extends CardImpl {
public AkkiRonin(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}");
this.subtype.add(SubType.GOBLIN);
this.subtype.add(SubType.SAMURAI);
this.power = new MageInt(1);
this.toughness = new MageInt(3);
// Whenever a Samurai or Warrior you control attacks alone, you may discard a card. If you do, draw a card.
this.addAbility(new AttacksAloneControlledTriggeredAbility(new DoIfCostPaid(
new DrawCardSourceControllerEffect(1), new DiscardCardCost()
), StaticFilters.FILTER_CONTROLLED_SAMURAI_OR_WARRIOR, false, false));
}
private AkkiRonin(final AkkiRonin card) {
super(card);
}
@Override
public AkkiRonin copy() {
return new AkkiRonin(this);
}
}

View file

@ -0,0 +1,54 @@
package mage.cards.a;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.AttachedToMatchesFilterCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.StaticFilters;
import mage.target.TargetPermanent;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class AkkiWarPaint extends CardImpl {
private static final Condition condition
= new AttachedToMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE);
public AkkiWarPaint(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}");
this.subtype.add(SubType.AURA);
// Enchant artifact or creature.
TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE);
this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
this.addAbility(new EnchantAbility(auraTarget.getTargetName()));
// As long as enchanted permanent is a creature, it gets +2/+1.
this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
new BoostEnchantedEffect(2, 1), condition,
"as long as enchanted permanent is a creature, it gets +2/+1"
)));
}
private AkkiWarPaint(final AkkiWarPaint card) {
super(card);
}
@Override
public AkkiWarPaint copy() {
return new AkkiWarPaint(this);
}
}

View file

@ -1,9 +1,9 @@
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
@ -13,8 +13,7 @@ import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.AttackingPredicate;
import mage.filter.common.FilterAttackingCreature;
/**
*
@ -22,13 +21,14 @@ import mage.filter.predicate.permanent.AttackingPredicate;
*/
public final class AkroanHoplite extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("attacking creatures you control");
private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking creatures you control");
static {
filter.add(TargetController.YOU.getControllerPredicate());
filter.add(AttackingPredicate.instance);
}
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null);
public AkroanHoplite(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}");
this.subtype.add(SubType.HUMAN);
@ -38,7 +38,7 @@ public final class AkroanHoplite extends CardImpl {
this.toughness = new MageInt(2);
// Whenever Akroan Hoplite attacks, it gets +X/+0 until end of turn, where X is the number of attacking creatures you control.
this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(new PermanentsOnBattlefieldCount(filter), StaticValue.get(0), Duration.EndOfTurn, true), false));
this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(xValue, StaticValue.get(0), Duration.EndOfTurn, true, "it"), false));
}
private AkroanHoplite(final AkroanHoplite card) {

View file

@ -77,7 +77,7 @@ class AkroanHorseChangeControlEffect extends OneShotEffect {
if (controller != null) {
if (controller.chooseTarget(outcome, target, source, game)) {
ContinuousEffect effect = new AkroanHorseGainControlEffect(Duration.Custom, target.getFirstTarget());
effect.setTargetPointer(new FixedTarget(source.getSourceId()));
effect.setTargetPointer(new FixedTarget(source.getSourceId(), game));
game.addEffect(effect, source);
return true;
}

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